summaryrefslogtreecommitdiffstats
path: root/libc-bottom-half
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 13:54:38 +0000
commit8c1ab65c0f548d20b7f177bdb736daaf603340e1 (patch)
treedf55b7e75bf43f2bf500845b105afe3ac3a5157e /libc-bottom-half
parentInitial commit. (diff)
downloadwasi-libc-b0f726bfa464c79fdf040fa7daed6094ddbffb4c.tar.xz
wasi-libc-b0f726bfa464c79fdf040fa7daed6094ddbffb4c.zip
Adding upstream version 0.0~git20221206.8b7148f.upstream/0.0_git20221206.8b7148f
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libc-bottom-half')
-rw-r--r--libc-bottom-half/README.md19
-rw-r--r--libc-bottom-half/clocks/clock.c35
-rw-r--r--libc-bottom-half/clocks/getrusage.c28
-rw-r--r--libc-bottom-half/clocks/times.c26
-rw-r--r--libc-bottom-half/cloudlibc/LICENSE22
-rw-r--r--libc-bottom-half/cloudlibc/src/common/clock.h19
-rw-r--r--libc-bottom-half/cloudlibc/src/common/limits.h35
-rw-r--r--libc-bottom-half/cloudlibc/src/common/time.h63
-rw-r--r--libc-bottom-half/cloudlibc/src/include/_/cdefs.h36
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c10
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c11
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c44
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c23
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c132
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c15
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c168
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c15
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c11
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/errno/errno.c87
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c62
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c80
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c13
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/poll/poll.c129
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c17
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c14
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c88
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c125
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/select/select.c25
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c48
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c40
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c32
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c27
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h57
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c21
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c29
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h116
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c38
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c40
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c40
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c12
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c12
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c21
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c22
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c37
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c15
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/time/time.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/close.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c53
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c16
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c22
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c24
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c25
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/pread.c31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c31
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/read.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c21
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c13
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c17
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c17
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c18
-rw-r--r--libc-bottom-half/cloudlibc/src/libc/unistd/write.c19
-rw-r--r--libc-bottom-half/crt/crt1-command.c53
-rw-r--r--libc-bottom-half/crt/crt1-reactor.c7
-rw-r--r--libc-bottom-half/crt/crt1.c3
-rw-r--r--libc-bottom-half/getpid/getpid.c6
-rw-r--r--libc-bottom-half/headers/private/_/limits.h1
-rw-r--r--libc-bottom-half/headers/private/_/struct/timespec.h1
-rw-r--r--libc-bottom-half/headers/private/_/struct/timeval.h1
-rw-r--r--libc-bottom-half/headers/private/_/types.h9
-rw-r--r--libc-bottom-half/headers/private/assert.h7
-rw-r--r--libc-bottom-half/headers/private/common/crt.h0
-rw-r--r--libc-bottom-half/headers/private/errno.h2
-rw-r--r--libc-bottom-half/headers/private/fcntl.h2
-rw-r--r--libc-bottom-half/headers/private/sched.h0
-rw-r--r--libc-bottom-half/headers/private/stdarg.h2
-rw-r--r--libc-bottom-half/headers/private/stdint.h12
-rw-r--r--libc-bottom-half/headers/private/stdio.h3
-rw-r--r--libc-bottom-half/headers/private/stdlib.h8
-rw-r--r--libc-bottom-half/headers/private/string.h2
-rw-r--r--libc-bottom-half/headers/private/sys/mman.h2
-rw-r--r--libc-bottom-half/headers/private/threads.h3
-rw-r--r--libc-bottom-half/headers/public/__errno.h20
-rw-r--r--libc-bottom-half/headers/public/__errno_values.h86
-rw-r--r--libc-bottom-half/headers/public/__fd_set.h76
-rw-r--r--libc-bottom-half/headers/public/__function___isatty.h14
-rw-r--r--libc-bottom-half/headers/public/__functions_malloc.h26
-rw-r--r--libc-bottom-half/headers/public/__functions_memcpy.h20
-rw-r--r--libc-bottom-half/headers/public/__header_dirent.h45
-rw-r--r--libc-bottom-half/headers/public/__header_fcntl.h61
-rw-r--r--libc-bottom-half/headers/public/__header_inttypes.h182
-rw-r--r--libc-bottom-half/headers/public/__header_netinet_in.h30
-rw-r--r--libc-bottom-half/headers/public/__header_poll.h27
-rw-r--r--libc-bottom-half/headers/public/__header_stdlib.h21
-rw-r--r--libc-bottom-half/headers/public/__header_string.h23
-rw-r--r--libc-bottom-half/headers/public/__header_sys_ioctl.h17
-rw-r--r--libc-bottom-half/headers/public/__header_sys_resource.h19
-rw-r--r--libc-bottom-half/headers/public/__header_sys_socket.h41
-rw-r--r--libc-bottom-half/headers/public/__header_sys_stat.h15
-rw-r--r--libc-bottom-half/headers/public/__header_time.h33
-rw-r--r--libc-bottom-half/headers/public/__header_unistd.h28
-rw-r--r--libc-bottom-half/headers/public/__macro_FD_SETSIZE.h6
-rw-r--r--libc-bottom-half/headers/public/__macro_PAGESIZE.h16
-rw-r--r--libc-bottom-half/headers/public/__mode_t.h38
-rw-r--r--libc-bottom-half/headers/public/__seek.h10
-rw-r--r--libc-bottom-half/headers/public/__struct_dirent.h14
-rw-r--r--libc-bottom-half/headers/public/__struct_in6_addr.h8
-rw-r--r--libc-bottom-half/headers/public/__struct_in_addr.h10
-rw-r--r--libc-bottom-half/headers/public/__struct_iovec.h12
-rw-r--r--libc-bottom-half/headers/public/__struct_msghdr.h16
-rw-r--r--libc-bottom-half/headers/public/__struct_pollfd.h10
-rw-r--r--libc-bottom-half/headers/public/__struct_rusage.h12
-rw-r--r--libc-bottom-half/headers/public/__struct_sockaddr.h14
-rw-r--r--libc-bottom-half/headers/public/__struct_sockaddr_in.h17
-rw-r--r--libc-bottom-half/headers/public/__struct_sockaddr_in6.h19
-rw-r--r--libc-bottom-half/headers/public/__struct_sockaddr_storage.h14
-rw-r--r--libc-bottom-half/headers/public/__struct_sockaddr_un.h13
-rw-r--r--libc-bottom-half/headers/public/__struct_stat.h35
-rw-r--r--libc-bottom-half/headers/public/__struct_timespec.h12
-rw-r--r--libc-bottom-half/headers/public/__struct_timeval.h13
-rw-r--r--libc-bottom-half/headers/public/__struct_tm.h19
-rw-r--r--libc-bottom-half/headers/public/__struct_tms.h13
-rw-r--r--libc-bottom-half/headers/public/__typedef_DIR.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_blkcnt_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_blksize_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_clock_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_clockid_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_dev_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_fd_set.h14
-rw-r--r--libc-bottom-half/headers/public/__typedef_gid_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_in_addr_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_in_port_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_ino_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_mode_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_nfds_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_nlink_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_off_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_sa_family_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_sigset_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_socklen_t.h6
-rw-r--r--libc-bottom-half/headers/public/__typedef_ssize_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_suseconds_t.h8
-rw-r--r--libc-bottom-half/headers/public/__typedef_time_t.h7
-rw-r--r--libc-bottom-half/headers/public/__typedef_uid_t.h6
-rw-r--r--libc-bottom-half/headers/public/dirent.h10
-rw-r--r--libc-bottom-half/headers/public/errno.h7
-rw-r--r--libc-bottom-half/headers/public/fcntl.h10
-rw-r--r--libc-bottom-half/headers/public/inttypes.h10
-rw-r--r--libc-bottom-half/headers/public/netinet/in.h6
-rw-r--r--libc-bottom-half/headers/public/poll.h10
-rw-r--r--libc-bottom-half/headers/public/stdlib.h10
-rw-r--r--libc-bottom-half/headers/public/string.h10
-rw-r--r--libc-bottom-half/headers/public/sys/ioctl.h10
-rw-r--r--libc-bottom-half/headers/public/sys/resource.h10
-rw-r--r--libc-bottom-half/headers/public/sys/select.h18
-rw-r--r--libc-bottom-half/headers/public/sys/socket.h6
-rw-r--r--libc-bottom-half/headers/public/sys/stat.h10
-rw-r--r--libc-bottom-half/headers/public/sys/time.h6
-rw-r--r--libc-bottom-half/headers/public/sys/times.h6
-rw-r--r--libc-bottom-half/headers/public/sys/types.h16
-rw-r--r--libc-bottom-half/headers/public/sys/uio.h6
-rw-r--r--libc-bottom-half/headers/public/sys/un.h6
-rw-r--r--libc-bottom-half/headers/public/time.h10
-rw-r--r--libc-bottom-half/headers/public/unistd.h10
-rw-r--r--libc-bottom-half/headers/public/wasi/api.h2115
-rw-r--r--libc-bottom-half/headers/public/wasi/libc-environ.h36
-rw-r--r--libc-bottom-half/headers/public/wasi/libc-find-relpath.h79
-rw-r--r--libc-bottom-half/headers/public/wasi/libc-nocwd.h58
-rw-r--r--libc-bottom-half/headers/public/wasi/libc.h65
-rw-r--r--libc-bottom-half/headers/public/wchar.h9
-rw-r--r--libc-bottom-half/mman/mman.c124
-rw-r--r--libc-bottom-half/signal/signal.c142
-rw-r--r--libc-bottom-half/sources/__errno_location.c5
-rw-r--r--libc-bottom-half/sources/__main_void.c54
-rw-r--r--libc-bottom-half/sources/__wasilibc_dt.c34
-rw-r--r--libc-bottom-half/sources/__wasilibc_environ.c14
-rw-r--r--libc-bottom-half/sources/__wasilibc_fd_renumber.c13
-rw-r--r--libc-bottom-half/sources/__wasilibc_initialize_environ.c93
-rw-r--r--libc-bottom-half/sources/__wasilibc_real.c672
-rw-r--r--libc-bottom-half/sources/__wasilibc_rmdirat.c12
-rw-r--r--libc-bottom-half/sources/__wasilibc_tell.c14
-rw-r--r--libc-bottom-half/sources/__wasilibc_unlinkat.c12
-rw-r--r--libc-bottom-half/sources/abort.c6
-rw-r--r--libc-bottom-half/sources/accept.c51
-rw-r--r--libc-bottom-half/sources/at_fdcwd.c141
-rw-r--r--libc-bottom-half/sources/chdir.c171
-rw-r--r--libc-bottom-half/sources/complex-builtins.c29
-rw-r--r--libc-bottom-half/sources/environ.c31
-rw-r--r--libc-bottom-half/sources/errno.c5
-rw-r--r--libc-bottom-half/sources/getcwd.c39
-rw-r--r--libc-bottom-half/sources/getentropy.c20
-rw-r--r--libc-bottom-half/sources/isatty.c22
-rw-r--r--libc-bottom-half/sources/math/fmin-fmax.c34
-rw-r--r--libc-bottom-half/sources/math/math-builtins.c69
-rw-r--r--libc-bottom-half/sources/posix.c449
-rw-r--r--libc-bottom-half/sources/preopens.c268
-rw-r--r--libc-bottom-half/sources/reallocarray.c14
-rw-r--r--libc-bottom-half/sources/sbrk.c32
-rw-r--r--libc-bottom-half/sources/truncate.c21
208 files changed, 9054 insertions, 0 deletions
diff --git a/libc-bottom-half/README.md b/libc-bottom-half/README.md
new file mode 100644
index 0000000..1e5401c
--- /dev/null
+++ b/libc-bottom-half/README.md
@@ -0,0 +1,19 @@
+# WASI libc "bottom half".
+
+The WASI libc "bottom half" is conceptually the lower half of a traditional libc
+implementation, consisting of C interfaces to the low-level WASI syscalls.
+
+This implementation is partially derived from the "bottom half" of [cloudlibc],
+revision 8835639f27fc42d32096d59d294a0bbb857dc368.
+
+[cloudlibc]: https://github.com/NuxiNL/cloudlibc
+
+This implementation includes preopen functionality, which emulates POSIX APIs
+accepting absolute paths by translating them into pre-opened directory handles
+and relative paths that can be opened with `openat`. This technique is inspired
+by [libpreopen], however the implementation here is designed to be built into
+libc rather than to be a layer on top of libc.
+
+[libpreopen]: https://github.com/musec/libpreopen
+
+The WASI libc lower half currently depends on the dlmalloc component.
diff --git a/libc-bottom-half/clocks/clock.c b/libc-bottom-half/clocks/clock.c
new file mode 100644
index 0000000..6767d73
--- /dev/null
+++ b/libc-bottom-half/clocks/clock.c
@@ -0,0 +1,35 @@
+#define _WASI_EMULATED_PROCESS_CLOCKS
+#include <time.h>
+#include <wasi/api.h>
+#include <common/time.h>
+
+_Static_assert(
+ CLOCKS_PER_SEC == NSEC_PER_SEC,
+ "This implementation assumes that `clock` is in nanoseconds"
+);
+
+// Snapshot of the monotonic clock at the start of the program.
+static __wasi_timestamp_t start;
+
+// Use a priority of 10 to run fairly early in the implementation-reserved
+// constructor priority range.
+__attribute__((constructor(10)))
+static void init(void) {
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, &start);
+}
+
+// Define the libc symbol as `__clock` so that we can reliably call it
+// from elsewhere in libc.
+clock_t __clock(void) {
+ // Use `MONOTONIC` instead of `PROCESS_CPUTIME_ID` since WASI doesn't have
+ // an inherent concept of a process. Note that this means we'll incorrectly
+ // include time from other processes, so this function is only declared by
+ // the headers if `_WASI_EMULATED_PROCESS_CLOCKS` is defined.
+ __wasi_timestamp_t now = 0;
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, &now);
+ return now - start;
+}
+
+// Define a user-visible alias as a weak symbol.
+__attribute__((__weak__, __alias__("__clock")))
+clock_t clock(void);
diff --git a/libc-bottom-half/clocks/getrusage.c b/libc-bottom-half/clocks/getrusage.c
new file mode 100644
index 0000000..d0113c5
--- /dev/null
+++ b/libc-bottom-half/clocks/getrusage.c
@@ -0,0 +1,28 @@
+#define _WASI_EMULATED_PROCESS_CLOCKS
+#include <sys/resource.h>
+#include <errno.h>
+#include <time.h>
+#include <wasi/api.h>
+#include <common/time.h>
+
+// `clock` is a weak symbol so that application code can override it.
+// We want to use the function in libc, so use the libc-internal name.
+clock_t __clock(void);
+
+int getrusage(int who, struct rusage *r_usage) {
+ switch (who) {
+ case RUSAGE_SELF: {
+ __wasi_timestamp_t usertime = __clock();
+ *r_usage = (struct rusage) {
+ .ru_utime = timestamp_to_timeval(usertime)
+ };
+ return 0;
+ }
+ case RUSAGE_CHILDREN:
+ *r_usage = (struct rusage) {};
+ return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/libc-bottom-half/clocks/times.c b/libc-bottom-half/clocks/times.c
new file mode 100644
index 0000000..e245569
--- /dev/null
+++ b/libc-bottom-half/clocks/times.c
@@ -0,0 +1,26 @@
+#define _WASI_EMULATED_PROCESS_CLOCKS
+#include <time.h>
+#include <sys/times.h>
+#include <wasi/api.h>
+#include <common/time.h>
+
+_Static_assert(
+ CLOCKS_PER_SEC == NSEC_PER_SEC,
+ "This implementation assumes that `clock` is in nanoseconds"
+);
+
+// `clock` is a weak symbol so that application code can override it.
+// We want to use the function in libc, so use the libc-internal name.
+clock_t __clock(void);
+
+clock_t times(struct tms *buffer) {
+ __wasi_timestamp_t user = __clock();
+ *buffer = (struct tms){
+ .tms_utime = user,
+ .tms_cutime = user
+ };
+
+ __wasi_timestamp_t realtime = 0;
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, &realtime);
+ return realtime;
+}
diff --git a/libc-bottom-half/cloudlibc/LICENSE b/libc-bottom-half/cloudlibc/LICENSE
new file mode 100644
index 0000000..dd33b05
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2015-2017 Nuxi (https://nuxi.nl/) and contributors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/libc-bottom-half/cloudlibc/src/common/clock.h b/libc-bottom-half/cloudlibc/src/common/clock.h
new file mode 100644
index 0000000..58d40a1
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/common/clock.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef COMMON_CLOCK_H
+#define COMMON_CLOCK_H
+
+#include <wasi/api.h>
+
+// In this implementation we define clockid_t as a pointer type, so that
+// we can implement them as full objects. Right now we only use those
+// objects to store the raw ABI-level clock identifier, but in the
+// future we can use this to provide support for pthread_getcpuclockid()
+// and clock file descriptors.
+struct __clockid {
+ __wasi_clockid_t id;
+};
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/common/limits.h b/libc-bottom-half/cloudlibc/src/common/limits.h
new file mode 100644
index 0000000..67e2f07
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/common/limits.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef COMMON_LIMITS_H
+#define COMMON_LIMITS_H
+
+#include <limits.h>
+
+#define NUMERIC_MIN(t) \
+ _Generic((t)0, char \
+ : CHAR_MIN, signed char \
+ : SCHAR_MIN, unsigned char : 0, short \
+ : SHRT_MIN, unsigned short : 0, int \
+ : INT_MIN, unsigned int : 0, long \
+ : LONG_MIN, unsigned long : 0, long long \
+ : LLONG_MIN, unsigned long long : 0, default \
+ : (void)0)
+
+#define NUMERIC_MAX(t) \
+ _Generic((t)0, char \
+ : CHAR_MAX, signed char \
+ : SCHAR_MAX, unsigned char \
+ : UCHAR_MAX, short \
+ : SHRT_MAX, unsigned short \
+ : USHRT_MAX, int \
+ : INT_MAX, unsigned int \
+ : UINT_MAX, long \
+ : LONG_MAX, unsigned long \
+ : ULONG_MAX, long long \
+ : LLONG_MAX, unsigned long long \
+ : ULLONG_MAX, default \
+ : (void)0)
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/common/time.h b/libc-bottom-half/cloudlibc/src/common/time.h
new file mode 100644
index 0000000..08e2852
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/common/time.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef COMMON_TIME_H
+#define COMMON_TIME_H
+
+#include <common/limits.h>
+
+#include <sys/time.h>
+
+#include <wasi/api.h>
+#include <stdbool.h>
+#include <time.h>
+
+#define NSEC_PER_SEC 1000000000
+
+static inline bool timespec_to_timestamp_exact(
+ const struct timespec *timespec, __wasi_timestamp_t *timestamp) {
+ // Invalid nanoseconds field.
+ if (timespec->tv_nsec < 0 || timespec->tv_nsec >= NSEC_PER_SEC)
+ return false;
+
+ // Timestamps before the Epoch are not supported.
+ if (timespec->tv_sec < 0)
+ return false;
+
+ // Make sure our timestamp does not overflow.
+ return !__builtin_mul_overflow(timespec->tv_sec, NSEC_PER_SEC, timestamp) &&
+ !__builtin_add_overflow(*timestamp, timespec->tv_nsec, timestamp);
+}
+
+static inline bool timespec_to_timestamp_clamp(
+ const struct timespec *timespec, __wasi_timestamp_t *timestamp) {
+ // Invalid nanoseconds field.
+ if (timespec->tv_nsec < 0 || timespec->tv_nsec >= NSEC_PER_SEC)
+ return false;
+
+ if (timespec->tv_sec < 0) {
+ // Timestamps before the Epoch are not supported.
+ *timestamp = 0;
+ } else if (__builtin_mul_overflow(timespec->tv_sec, NSEC_PER_SEC, timestamp) ||
+ __builtin_add_overflow(*timestamp, timespec->tv_nsec, timestamp)) {
+ // Make sure our timestamp does not overflow.
+ *timestamp = NUMERIC_MAX(__wasi_timestamp_t);
+ }
+ return true;
+}
+
+static inline struct timespec timestamp_to_timespec(
+ __wasi_timestamp_t timestamp) {
+ // Decompose timestamp into seconds and nanoseconds.
+ return (struct timespec){.tv_sec = timestamp / NSEC_PER_SEC,
+ .tv_nsec = timestamp % NSEC_PER_SEC};
+}
+
+static inline struct timeval timestamp_to_timeval(
+ __wasi_timestamp_t timestamp) {
+ struct timespec ts = timestamp_to_timespec(timestamp);
+ return (struct timeval){.tv_sec = ts.tv_sec, ts.tv_nsec / 1000};
+}
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/include/_/cdefs.h b/libc-bottom-half/cloudlibc/src/include/_/cdefs.h
new file mode 100644
index 0000000..d9a6f54
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/include/_/cdefs.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#ifndef ___CDEFS_H_
+#define ___CDEFS_H_
+
+// Compiler-independent annotations.
+
+#define __strong_reference(oldsym, newsym) \
+ extern __typeof__(oldsym) newsym __attribute__((__alias__(#oldsym)))
+
+// Convenience macros.
+
+#define __arraycount(x) (sizeof(x) / sizeof((x)[0]))
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c
new file mode 100644
index 0000000..3585737
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/closedir.c
@@ -0,0 +1,10 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+#include <unistd.h>
+
+int closedir(DIR *dirp) {
+ return close(fdclosedir(dirp));
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h
new file mode 100644
index 0000000..642400a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirent_impl.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef DIRENT_DIRENT_IMPL_H
+#define DIRENT_DIRENT_IMPL_H
+
+#include <wasi/api.h>
+#include <stddef.h>
+
+struct dirent;
+
+#define DIRENT_DEFAULT_BUFFER_SIZE 4096
+
+struct _DIR {
+ // Directory file descriptor and cookie.
+ int fd;
+ __wasi_dircookie_t cookie;
+
+ // Read buffer.
+ char *buffer;
+ size_t buffer_processed;
+ size_t buffer_size;
+ size_t buffer_used;
+
+ // Object returned by readdir().
+ struct dirent *dirent;
+ size_t dirent_size;
+};
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c b/libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c
new file mode 100644
index 0000000..033157a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/dirfd.c
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+int dirfd(DIR *dirp) {
+ return dirp->fd;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c
new file mode 100644
index 0000000..cabe702
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdclosedir.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+#include <stdlib.h>
+
+#include "dirent_impl.h"
+
+int fdclosedir(DIR *dirp) {
+ int fd = dirp->fd;
+ free(dirp->buffer);
+ free(dirp->dirent);
+ free(dirp);
+ return fd;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c
new file mode 100644
index 0000000..4a2136a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/fdopendir.c
@@ -0,0 +1,44 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "dirent_impl.h"
+
+DIR *fdopendir(int fd) {
+ // Allocate new directory object and read buffer.
+ DIR *dirp = malloc(sizeof(*dirp));
+ if (dirp == NULL)
+ return NULL;
+ dirp->buffer = malloc(DIRENT_DEFAULT_BUFFER_SIZE);
+ if (dirp->buffer == NULL) {
+ free(dirp);
+ return NULL;
+ }
+
+ // Ensure that this is really a directory by already loading the first
+ // chunk of data.
+ __wasi_errno_t error =
+ // TODO: Remove the cast on `dirp->buffer` once the witx is updated with char8 support.
+ __wasi_fd_readdir(fd, (uint8_t *)dirp->buffer, DIRENT_DEFAULT_BUFFER_SIZE,
+ __WASI_DIRCOOKIE_START, &dirp->buffer_used);
+ if (error != 0) {
+ free(dirp->buffer);
+ free(dirp);
+ errno = error;
+ return NULL;
+ }
+
+ // Initialize other members.
+ dirp->fd = fd;
+ dirp->cookie = __WASI_DIRCOOKIE_START;
+ dirp->buffer_processed = 0;
+ dirp->buffer_size = DIRENT_DEFAULT_BUFFER_SIZE;
+ dirp->dirent = NULL;
+ dirp->dirent_size = 1;
+ return dirp;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c b/libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c
new file mode 100644
index 0000000..e6d1c2a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/opendirat.c
@@ -0,0 +1,23 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/libc.h>
+#include <wasi/libc-nocwd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+
+DIR *__wasilibc_nocwd_opendirat(int dir, const char *dirname) {
+ // Open directory.
+ int fd = __wasilibc_nocwd_openat_nomode(dir, dirname, O_RDONLY | O_NONBLOCK | O_DIRECTORY);
+ if (fd == -1)
+ return NULL;
+
+ // Create directory handle.
+ DIR *result = fdopendir(fd);
+ if (result == NULL)
+ close(fd);
+ return result;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
new file mode 100644
index 0000000..bfd1921
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/readdir.c
@@ -0,0 +1,132 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dirent_impl.h"
+
+static_assert(DT_BLK == __WASI_FILETYPE_BLOCK_DEVICE, "Value mismatch");
+static_assert(DT_CHR == __WASI_FILETYPE_CHARACTER_DEVICE, "Value mismatch");
+static_assert(DT_DIR == __WASI_FILETYPE_DIRECTORY, "Value mismatch");
+static_assert(DT_FIFO == __WASI_FILETYPE_SOCKET_STREAM, "Value mismatch");
+static_assert(DT_LNK == __WASI_FILETYPE_SYMBOLIC_LINK, "Value mismatch");
+static_assert(DT_REG == __WASI_FILETYPE_REGULAR_FILE, "Value mismatch");
+static_assert(DT_UNKNOWN == __WASI_FILETYPE_UNKNOWN, "Value mismatch");
+
+// Grows a buffer to be large enough to hold a certain amount of data.
+#define GROW(buffer, buffer_size, target_size) \
+ do { \
+ if ((buffer_size) < (target_size)) { \
+ size_t new_size = (buffer_size); \
+ while (new_size < (target_size)) \
+ new_size *= 2; \
+ void *new_buffer = realloc(buffer, new_size); \
+ if (new_buffer == NULL) \
+ return NULL; \
+ (buffer) = new_buffer; \
+ (buffer_size) = new_size; \
+ } \
+ } while (0)
+
+struct dirent *readdir(DIR *dirp) {
+ for (;;) {
+ // Extract the next dirent header.
+ size_t buffer_left = dirp->buffer_used - dirp->buffer_processed;
+ if (buffer_left < sizeof(__wasi_dirent_t)) {
+ // End-of-file.
+ if (dirp->buffer_used < dirp->buffer_size)
+ return NULL;
+ goto read_entries;
+ }
+ __wasi_dirent_t entry;
+ memcpy(&entry, dirp->buffer + dirp->buffer_processed, sizeof(entry));
+
+ size_t entry_size = sizeof(__wasi_dirent_t) + entry.d_namlen;
+ if (entry.d_namlen == 0) {
+ // Invalid pathname length. Skip the entry.
+ dirp->buffer_processed += entry_size;
+ continue;
+ }
+
+ // The entire entry must be present in buffer space. If not, read
+ // the entry another time. Ensure that the read buffer is large
+ // enough to fit at least this single entry.
+ if (buffer_left < entry_size) {
+ GROW(dirp->buffer, dirp->buffer_size, entry_size);
+ goto read_entries;
+ }
+
+ // Skip entries having null bytes in the filename.
+ const char *name = dirp->buffer + dirp->buffer_processed + sizeof(entry);
+ if (memchr(name, '\0', entry.d_namlen) != NULL) {
+ dirp->buffer_processed += entry_size;
+ continue;
+ }
+
+ // Return the next directory entry. Ensure that the dirent is large
+ // enough to fit the filename.
+ GROW(dirp->dirent, dirp->dirent_size,
+ offsetof(struct dirent, d_name) + entry.d_namlen + 1);
+ struct dirent *dirent = dirp->dirent;
+ dirent->d_type = entry.d_type;
+ memcpy(dirent->d_name, name, entry.d_namlen);
+ dirent->d_name[entry.d_namlen] = '\0';
+
+ // `fd_readdir` implementations may set the inode field to zero if the
+ // the inode number is unknown. In that case, do an `fstatat` to get the
+ // inode number.
+ off_t d_ino = entry.d_ino;
+ unsigned char d_type = entry.d_type;
+ if (d_ino == 0 && strcmp(dirent->d_name, "..") != 0) {
+ struct stat statbuf;
+ if (fstatat(dirp->fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (errno == ENOENT) {
+ // The file disappeared before we could read it, so skip it.
+ dirp->buffer_processed += entry_size;
+ continue;
+ }
+ return NULL;
+ }
+
+ // Fill in the inode.
+ d_ino = statbuf.st_ino;
+
+ // In case someone raced with us and replaced the object with this name
+ // with another of a different type, update the type too.
+ d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
+ }
+ dirent->d_ino = d_ino;
+ dirent->d_type = d_type;
+
+ dirp->cookie = entry.d_next;
+ dirp->buffer_processed += entry_size;
+ return dirent;
+
+ read_entries:
+ // Discard data currently stored in the input buffer.
+ dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
+
+ // Load more directory entries and continue.
+ __wasi_errno_t error =
+ // TODO: Remove the cast on `dirp->buffer` once the witx is updated with char8 support.
+ __wasi_fd_readdir(dirp->fd, (uint8_t *)dirp->buffer, dirp->buffer_size,
+ dirp->cookie, &dirp->buffer_used);
+ if (error != 0) {
+ errno = error;
+ return NULL;
+ }
+ dirp->buffer_processed = 0;
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c
new file mode 100644
index 0000000..8dd5882
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/rewinddir.c
@@ -0,0 +1,15 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+void rewinddir(DIR *dirp) {
+ // Update cookie.
+ dirp->cookie = __WASI_DIRCOOKIE_START;
+ // Mark entire buffer as processed to force a read of new data.
+ dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c b/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c
new file mode 100644
index 0000000..079b3b3
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/scandirat.c
@@ -0,0 +1,168 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <wasi/libc-nocwd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "dirent_impl.h"
+
+static int sel_true(const struct dirent *de) {
+ return 1;
+}
+
+int __wasilibc_nocwd_scandirat(int dirfd, const char *dir, struct dirent ***namelist,
+ int (*sel)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **)) {
+ struct stat statbuf;
+
+ // Match all files if no select function is provided.
+ if (sel == NULL)
+ sel = sel_true;
+
+ // Open the directory.
+ int fd = __wasilibc_nocwd_openat_nomode(dirfd, dir, O_RDONLY | O_NONBLOCK | O_DIRECTORY);
+ if (fd == -1)
+ return -1;
+
+ // Allocate a read buffer for the directory entries.
+ size_t buffer_size = DIRENT_DEFAULT_BUFFER_SIZE;
+ char *buffer = malloc(buffer_size);
+ if (buffer == NULL) {
+ close(fd);
+ return -1;
+ }
+ size_t buffer_processed = buffer_size;
+ size_t buffer_used = buffer_size;
+
+ // Space for the array to return to the caller.
+ struct dirent **dirents = NULL;
+ size_t dirents_size = 0;
+ size_t dirents_used = 0;
+
+ __wasi_dircookie_t cookie = __WASI_DIRCOOKIE_START;
+ for (;;) {
+ // Extract the next dirent header.
+ size_t buffer_left = buffer_used - buffer_processed;
+ if (buffer_left < sizeof(__wasi_dirent_t)) {
+ // End-of-file.
+ if (buffer_used < buffer_size)
+ break;
+ goto read_entries;
+ }
+ __wasi_dirent_t entry;
+ memcpy(&entry, buffer + buffer_processed, sizeof(entry));
+
+ size_t entry_size = sizeof(__wasi_dirent_t) + entry.d_namlen;
+ if (entry.d_namlen == 0) {
+ // Invalid pathname length. Skip the entry.
+ buffer_processed += entry_size;
+ continue;
+ }
+
+ // The entire entry must be present in buffer space. If not, read
+ // the entry another time. Ensure that the read buffer is large
+ // enough to fit at least this single entry.
+ if (buffer_left < entry_size) {
+ while (buffer_size < entry_size)
+ buffer_size *= 2;
+ char *new_buffer = realloc(buffer, buffer_size);
+ if (new_buffer == NULL)
+ goto bad;
+ buffer = new_buffer;
+ goto read_entries;
+ }
+
+ // Skip entries having null bytes in the filename.
+ const char *name = buffer + buffer_processed + sizeof(entry);
+ buffer_processed += entry_size;
+ if (memchr(name, '\0', entry.d_namlen) != NULL)
+ continue;
+
+ // Create the new directory entry.
+ struct dirent *dirent =
+ malloc(offsetof(struct dirent, d_name) + entry.d_namlen + 1);
+ if (dirent == NULL)
+ goto bad;
+ dirent->d_type = entry.d_type;
+ memcpy(dirent->d_name, name, entry.d_namlen);
+ dirent->d_name[entry.d_namlen] = '\0';
+
+ // `fd_readdir` implementations may set the inode field to zero if the
+ // the inode number is unknown. In that case, do an `fstatat` to get the
+ // inode number.
+ off_t d_ino = entry.d_ino;
+ unsigned char d_type = entry.d_type;
+ if (d_ino == 0) {
+ if (fstatat(fd, dirent->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) != 0) {
+ return -1;
+ }
+
+ // Fill in the inode.
+ d_ino = statbuf.st_ino;
+
+ // In case someone raced with us and replaced the object with this name
+ // with another of a different type, update the type too.
+ d_type = __wasilibc_iftodt(statbuf.st_mode & S_IFMT);
+ }
+ dirent->d_ino = d_ino;
+ dirent->d_type = d_type;
+
+ cookie = entry.d_next;
+
+ if (sel(dirent)) {
+ // Add the entry to the results.
+ if (dirents_used == dirents_size) {
+ dirents_size = dirents_size < 8 ? 8 : dirents_size * 2;
+ struct dirent **new_dirents =
+ realloc(dirents, dirents_size * sizeof(*dirents));
+ if (new_dirents == NULL) {
+ free(dirent);
+ goto bad;
+ }
+ dirents = new_dirents;
+ }
+ dirents[dirents_used++] = dirent;
+ } else {
+ // Discard the entry.
+ free(dirent);
+ }
+ continue;
+
+ read_entries:;
+ // Load more directory entries and continue.
+ // TODO: Remove the cast on `buffer` once the witx is updated with char8 support.
+ __wasi_errno_t error = __wasi_fd_readdir(fd, (uint8_t *)buffer, buffer_size,
+ cookie, &buffer_used);
+ if (error != 0) {
+ errno = error;
+ goto bad;
+ }
+ buffer_processed = 0;
+ }
+
+ // Sort results and return them.
+ free(buffer);
+ close(fd);
+ (qsort)(dirents, dirents_used, sizeof(*dirents),
+ (int (*)(const void *, const void *))compar);
+ *namelist = dirents;
+ return dirents_used;
+
+bad:
+ // Deallocate partially created results.
+ for (size_t i = 0; i < dirents_used; ++i)
+ free(dirents[i]);
+ free(dirents);
+ free(buffer);
+ close(fd);
+ return -1;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c
new file mode 100644
index 0000000..a865d92
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/seekdir.c
@@ -0,0 +1,15 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+void seekdir(DIR *dirp, long loc) {
+ // Update cookie.
+ dirp->cookie = (unsigned long)loc;
+ // Mark entire buffer as processed to force a read of new data.
+ // TODO(ed): We could prevent a read if the offset is in the buffer.
+ dirp->buffer_used = dirp->buffer_processed = dirp->buffer_size;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c b/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c
new file mode 100644
index 0000000..05687ee
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/dirent/telldir.c
@@ -0,0 +1,11 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <dirent.h>
+
+#include "dirent_impl.h"
+
+long telldir(DIR *dirp) {
+ return dirp->cookie;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/errno/errno.c b/libc-bottom-half/cloudlibc/src/libc/errno/errno.c
new file mode 100644
index 0000000..d7a27f3
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/errno/errno.c
@@ -0,0 +1,87 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <threads.h>
+
+static_assert(E2BIG == __WASI_ERRNO_2BIG, "Value mismatch");
+static_assert(EACCES == __WASI_ERRNO_ACCES, "Value mismatch");
+static_assert(EADDRINUSE == __WASI_ERRNO_ADDRINUSE, "Value mismatch");
+static_assert(EADDRNOTAVAIL == __WASI_ERRNO_ADDRNOTAVAIL, "Value mismatch");
+static_assert(EAFNOSUPPORT == __WASI_ERRNO_AFNOSUPPORT, "Value mismatch");
+static_assert(EAGAIN == __WASI_ERRNO_AGAIN, "Value mismatch");
+static_assert(EALREADY == __WASI_ERRNO_ALREADY, "Value mismatch");
+static_assert(EBADF == __WASI_ERRNO_BADF, "Value mismatch");
+static_assert(EBADMSG == __WASI_ERRNO_BADMSG, "Value mismatch");
+static_assert(EBUSY == __WASI_ERRNO_BUSY, "Value mismatch");
+static_assert(ECANCELED == __WASI_ERRNO_CANCELED, "Value mismatch");
+static_assert(ECHILD == __WASI_ERRNO_CHILD, "Value mismatch");
+static_assert(ECONNABORTED == __WASI_ERRNO_CONNABORTED, "Value mismatch");
+static_assert(ECONNREFUSED == __WASI_ERRNO_CONNREFUSED, "Value mismatch");
+static_assert(ECONNRESET == __WASI_ERRNO_CONNRESET, "Value mismatch");
+static_assert(EDEADLK == __WASI_ERRNO_DEADLK, "Value mismatch");
+static_assert(EDESTADDRREQ == __WASI_ERRNO_DESTADDRREQ, "Value mismatch");
+static_assert(EDOM == __WASI_ERRNO_DOM, "Value mismatch");
+static_assert(EDQUOT == __WASI_ERRNO_DQUOT, "Value mismatch");
+static_assert(EEXIST == __WASI_ERRNO_EXIST, "Value mismatch");
+static_assert(EFAULT == __WASI_ERRNO_FAULT, "Value mismatch");
+static_assert(EFBIG == __WASI_ERRNO_FBIG, "Value mismatch");
+static_assert(EHOSTUNREACH == __WASI_ERRNO_HOSTUNREACH, "Value mismatch");
+static_assert(EIDRM == __WASI_ERRNO_IDRM, "Value mismatch");
+static_assert(EILSEQ == __WASI_ERRNO_ILSEQ, "Value mismatch");
+static_assert(EINPROGRESS == __WASI_ERRNO_INPROGRESS, "Value mismatch");
+static_assert(EINTR == __WASI_ERRNO_INTR, "Value mismatch");
+static_assert(EINVAL == __WASI_ERRNO_INVAL, "Value mismatch");
+static_assert(EIO == __WASI_ERRNO_IO, "Value mismatch");
+static_assert(EISCONN == __WASI_ERRNO_ISCONN, "Value mismatch");
+static_assert(EISDIR == __WASI_ERRNO_ISDIR, "Value mismatch");
+static_assert(ELOOP == __WASI_ERRNO_LOOP, "Value mismatch");
+static_assert(EMFILE == __WASI_ERRNO_MFILE, "Value mismatch");
+static_assert(EMLINK == __WASI_ERRNO_MLINK, "Value mismatch");
+static_assert(EMSGSIZE == __WASI_ERRNO_MSGSIZE, "Value mismatch");
+static_assert(EMULTIHOP == __WASI_ERRNO_MULTIHOP, "Value mismatch");
+static_assert(ENAMETOOLONG == __WASI_ERRNO_NAMETOOLONG, "Value mismatch");
+static_assert(ENETDOWN == __WASI_ERRNO_NETDOWN, "Value mismatch");
+static_assert(ENETRESET == __WASI_ERRNO_NETRESET, "Value mismatch");
+static_assert(ENETUNREACH == __WASI_ERRNO_NETUNREACH, "Value mismatch");
+static_assert(ENFILE == __WASI_ERRNO_NFILE, "Value mismatch");
+static_assert(ENOBUFS == __WASI_ERRNO_NOBUFS, "Value mismatch");
+static_assert(ENODEV == __WASI_ERRNO_NODEV, "Value mismatch");
+static_assert(ENOENT == __WASI_ERRNO_NOENT, "Value mismatch");
+static_assert(ENOEXEC == __WASI_ERRNO_NOEXEC, "Value mismatch");
+static_assert(ENOLCK == __WASI_ERRNO_NOLCK, "Value mismatch");
+static_assert(ENOLINK == __WASI_ERRNO_NOLINK, "Value mismatch");
+static_assert(ENOMEM == __WASI_ERRNO_NOMEM, "Value mismatch");
+static_assert(ENOMSG == __WASI_ERRNO_NOMSG, "Value mismatch");
+static_assert(ENOPROTOOPT == __WASI_ERRNO_NOPROTOOPT, "Value mismatch");
+static_assert(ENOSPC == __WASI_ERRNO_NOSPC, "Value mismatch");
+static_assert(ENOSYS == __WASI_ERRNO_NOSYS, "Value mismatch");
+static_assert(ENOTCAPABLE == __WASI_ERRNO_NOTCAPABLE, "Value mismatch");
+static_assert(ENOTCONN == __WASI_ERRNO_NOTCONN, "Value mismatch");
+static_assert(ENOTDIR == __WASI_ERRNO_NOTDIR, "Value mismatch");
+static_assert(ENOTEMPTY == __WASI_ERRNO_NOTEMPTY, "Value mismatch");
+static_assert(ENOTRECOVERABLE == __WASI_ERRNO_NOTRECOVERABLE, "Value mismatch");
+static_assert(ENOTSOCK == __WASI_ERRNO_NOTSOCK, "Value mismatch");
+static_assert(ENOTSUP == __WASI_ERRNO_NOTSUP, "Value mismatch");
+static_assert(ENOTTY == __WASI_ERRNO_NOTTY, "Value mismatch");
+static_assert(ENXIO == __WASI_ERRNO_NXIO, "Value mismatch");
+static_assert(EOVERFLOW == __WASI_ERRNO_OVERFLOW, "Value mismatch");
+static_assert(EOWNERDEAD == __WASI_ERRNO_OWNERDEAD, "Value mismatch");
+static_assert(EPERM == __WASI_ERRNO_PERM, "Value mismatch");
+static_assert(EPIPE == __WASI_ERRNO_PIPE, "Value mismatch");
+static_assert(EPROTO == __WASI_ERRNO_PROTO, "Value mismatch");
+static_assert(EPROTONOSUPPORT == __WASI_ERRNO_PROTONOSUPPORT, "Value mismatch");
+static_assert(EPROTOTYPE == __WASI_ERRNO_PROTOTYPE, "Value mismatch");
+static_assert(ERANGE == __WASI_ERRNO_RANGE, "Value mismatch");
+static_assert(EROFS == __WASI_ERRNO_ROFS, "Value mismatch");
+static_assert(ESPIPE == __WASI_ERRNO_SPIPE, "Value mismatch");
+static_assert(ESRCH == __WASI_ERRNO_SRCH, "Value mismatch");
+static_assert(ESTALE == __WASI_ERRNO_STALE, "Value mismatch");
+static_assert(ETIMEDOUT == __WASI_ERRNO_TIMEDOUT, "Value mismatch");
+static_assert(ETXTBSY == __WASI_ERRNO_TXTBSY, "Value mismatch");
+static_assert(EXDEV == __WASI_ERRNO_XDEV, "Value mismatch");
+
+thread_local int errno = 0;
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c
new file mode 100644
index 0000000..5d4055d
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/fcntl.c
@@ -0,0 +1,62 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+
+int fcntl(int fildes, int cmd, ...) {
+ switch (cmd) {
+ case F_GETFD:
+ // Act as if the close-on-exec flag is always set.
+ return FD_CLOEXEC;
+ case F_SETFD:
+ // The close-on-exec flag is ignored.
+ return 0;
+ case F_GETFL: {
+ // Obtain the flags and the rights of the descriptor.
+ __wasi_fdstat_t fds;
+ __wasi_errno_t error = __wasi_fd_fdstat_get(fildes, &fds);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Roughly approximate the access mode by converting the rights.
+ int oflags = fds.fs_flags;
+ if ((fds.fs_rights_base &
+ (__WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_READDIR)) != 0) {
+ if ((fds.fs_rights_base & __WASI_RIGHTS_FD_WRITE) != 0)
+ oflags |= O_RDWR;
+ else
+ oflags |= O_RDONLY;
+ } else if ((fds.fs_rights_base & __WASI_RIGHTS_FD_WRITE) != 0) {
+ oflags |= O_WRONLY;
+ } else {
+ oflags |= O_SEARCH;
+ }
+ return oflags;
+ }
+ case F_SETFL: {
+ // Set new file descriptor flags.
+ va_list ap;
+ va_start(ap, cmd);
+ int flags = va_arg(ap, int);
+ va_end(ap);
+
+ __wasi_fdflags_t fs_flags = flags & 0xfff;
+ __wasi_errno_t error =
+ __wasi_fd_fdstat_set_flags(fildes, fs_flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+ }
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c
new file mode 100644
index 0000000..09cbbf8
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/openat.c
@@ -0,0 +1,80 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+static_assert(O_APPEND == __WASI_FDFLAGS_APPEND, "Value mismatch");
+static_assert(O_DSYNC == __WASI_FDFLAGS_DSYNC, "Value mismatch");
+static_assert(O_NONBLOCK == __WASI_FDFLAGS_NONBLOCK, "Value mismatch");
+static_assert(O_RSYNC == __WASI_FDFLAGS_RSYNC, "Value mismatch");
+static_assert(O_SYNC == __WASI_FDFLAGS_SYNC, "Value mismatch");
+
+static_assert(O_CREAT >> 12 == __WASI_OFLAGS_CREAT, "Value mismatch");
+static_assert(O_DIRECTORY >> 12 == __WASI_OFLAGS_DIRECTORY, "Value mismatch");
+static_assert(O_EXCL >> 12 == __WASI_OFLAGS_EXCL, "Value mismatch");
+static_assert(O_TRUNC >> 12 == __WASI_OFLAGS_TRUNC, "Value mismatch");
+
+int __wasilibc_nocwd_openat_nomode(int fd, const char *path, int oflag) {
+ // Compute rights corresponding with the access modes provided.
+ // Attempt to obtain all rights, except the ones that contradict the
+ // access mode provided to openat().
+ __wasi_rights_t max =
+ ~(__WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_READ |
+ __WASI_RIGHTS_FD_WRITE | __WASI_RIGHTS_FD_ALLOCATE |
+ __WASI_RIGHTS_FD_READDIR | __WASI_RIGHTS_FD_FILESTAT_SET_SIZE);
+ switch (oflag & O_ACCMODE) {
+ case O_RDONLY:
+ case O_RDWR:
+ case O_WRONLY:
+ if ((oflag & O_RDONLY) != 0) {
+ max |= __WASI_RIGHTS_FD_READ | __WASI_RIGHTS_FD_READDIR;
+ }
+ if ((oflag & O_WRONLY) != 0) {
+ max |= __WASI_RIGHTS_FD_DATASYNC | __WASI_RIGHTS_FD_WRITE |
+ __WASI_RIGHTS_FD_ALLOCATE |
+ __WASI_RIGHTS_FD_FILESTAT_SET_SIZE;
+ }
+ break;
+ case O_EXEC:
+ break;
+ case O_SEARCH:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Ensure that we can actually obtain the minimal rights needed.
+ __wasi_fdstat_t fsb_cur;
+ __wasi_errno_t error = __wasi_fd_fdstat_get(fd, &fsb_cur);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Path lookup properties.
+ __wasi_lookupflags_t lookup_flags = 0;
+ if ((oflag & O_NOFOLLOW) == 0)
+ lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Open file with appropriate rights.
+ __wasi_fdflags_t fs_flags = oflag & 0xfff;
+ __wasi_rights_t fs_rights_base = max & fsb_cur.fs_rights_inheriting;
+ __wasi_rights_t fs_rights_inheriting = fsb_cur.fs_rights_inheriting;
+ __wasi_fd_t newfd;
+ error = __wasi_path_open(fd, lookup_flags, path,
+ (oflag >> 12) & 0xfff,
+ fs_rights_base, fs_rights_inheriting, fs_flags,
+ &newfd);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return newfd;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c
new file mode 100644
index 0000000..d683d39
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fadvise.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+
+static_assert(POSIX_FADV_DONTNEED == __WASI_ADVICE_DONTNEED,
+ "Value mismatch");
+static_assert(POSIX_FADV_NOREUSE == __WASI_ADVICE_NOREUSE, "Value mismatch");
+static_assert(POSIX_FADV_NORMAL == __WASI_ADVICE_NORMAL, "Value mismatch");
+static_assert(POSIX_FADV_RANDOM == __WASI_ADVICE_RANDOM, "Value mismatch");
+static_assert(POSIX_FADV_SEQUENTIAL == __WASI_ADVICE_SEQUENTIAL,
+ "Value mismatch");
+static_assert(POSIX_FADV_WILLNEED == __WASI_ADVICE_WILLNEED,
+ "Value mismatch");
+
+int posix_fadvise(int fd, off_t offset, off_t len, int advice) {
+ if (offset < 0 || len < 0)
+ return EINVAL;
+ return __wasi_fd_advise(fd, offset, len, advice);
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c
new file mode 100644
index 0000000..4b41c4b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/fcntl/posix_fallocate.c
@@ -0,0 +1,13 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+
+int posix_fallocate(int fd, off_t offset, off_t len) {
+ if (offset < 0 || len < 0)
+ return EINVAL;
+ return __wasi_fd_allocate(fd, offset, len);
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/poll/poll.c b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
new file mode 100644
index 0000000..cde4e81
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/poll/poll.c
@@ -0,0 +1,129 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdbool.h>
+
+int poll(struct pollfd *fds, size_t nfds, int timeout) {
+ // Construct events for poll().
+ size_t maxevents = 2 * nfds + 1;
+ __wasi_subscription_t subscriptions[maxevents];
+ size_t nsubscriptions = 0;
+ for (size_t i = 0; i < nfds; ++i) {
+ struct pollfd *pollfd = &fds[i];
+ if (pollfd->fd < 0)
+ continue;
+ bool created_events = false;
+ if ((pollfd->events & POLLRDNORM) != 0) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = (uintptr_t)pollfd,
+ .u.tag = __WASI_EVENTTYPE_FD_READ,
+ .u.u.fd_read.file_descriptor = pollfd->fd,
+ };
+ created_events = true;
+ }
+ if ((pollfd->events & POLLWRNORM) != 0) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = (uintptr_t)pollfd,
+ .u.tag = __WASI_EVENTTYPE_FD_WRITE,
+ .u.u.fd_write.file_descriptor = pollfd->fd,
+ };
+ created_events = true;
+ }
+
+ // As entries are decomposed into separate read/write subscriptions,
+ // we cannot detect POLLERR, POLLHUP and POLLNVAL if POLLRDNORM and
+ // POLLWRNORM are not specified. Disallow this for now.
+ if (!created_events) {
+ errno = ENOSYS;
+ return -1;
+ }
+ }
+
+ // Create extra event for the timeout.
+ if (timeout >= 0) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = __WASI_CLOCKID_REALTIME,
+ .u.u.clock.timeout = (__wasi_timestamp_t)timeout * 1000000,
+ };
+ }
+
+ // Execute poll().
+ size_t nevents;
+ __wasi_event_t events[nsubscriptions];
+ __wasi_errno_t error =
+ __wasi_poll_oneoff(subscriptions, events, nsubscriptions, &nevents);
+ if (error != 0) {
+ // WASI's poll requires at least one subscription, or else it returns
+ // `EINVAL`. Since a `poll` with nothing to wait for is valid in POSIX,
+ // return `ENOTSUP` to indicate that we don't support that case.
+ //
+ // Wasm has no signal handling, so if none of the user-provided `pollfd`
+ // elements, nor the timeout, led us to producing even one subscription
+ // to wait for, there would be no way for the poll to wake up. WASI
+ // returns `EINVAL` in this case, but for users of `poll`, `ENOTSUP` is
+ // more likely to be understood.
+ if (nsubscriptions == 0)
+ errno = ENOTSUP;
+ else
+ errno = error;
+ return -1;
+ }
+
+ // Clear revents fields.
+ for (size_t i = 0; i < nfds; ++i) {
+ struct pollfd *pollfd = &fds[i];
+ pollfd->revents = 0;
+ }
+
+ // Set revents fields.
+ for (size_t i = 0; i < nevents; ++i) {
+ const __wasi_event_t *event = &events[i];
+ if (event->type == __WASI_EVENTTYPE_FD_READ ||
+ event->type == __WASI_EVENTTYPE_FD_WRITE) {
+ struct pollfd *pollfd = (struct pollfd *)(uintptr_t)event->userdata;
+ if (event->error == __WASI_ERRNO_BADF) {
+ // Invalid file descriptor.
+ pollfd->revents |= POLLNVAL;
+ } else if (event->error == __WASI_ERRNO_PIPE) {
+ // Hangup on write side of pipe.
+ pollfd->revents |= POLLHUP;
+ } else if (event->error != 0) {
+ // Another error occurred.
+ pollfd->revents |= POLLERR;
+ } else {
+ // Data can be read or written.
+ if (event->type == __WASI_EVENTTYPE_FD_READ) {
+ pollfd->revents |= POLLRDNORM;
+ if (event->fd_readwrite.flags & __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP) {
+ pollfd->revents |= POLLHUP;
+ }
+ } else if (event->type == __WASI_EVENTTYPE_FD_WRITE) {
+ pollfd->revents |= POLLWRNORM;
+ if (event->fd_readwrite.flags & __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP) {
+ pollfd->revents |= POLLHUP;
+ }
+ }
+ }
+ }
+ }
+
+ // Return the number of events with a non-zero revents value.
+ int retval = 0;
+ for (size_t i = 0; i < nfds; ++i) {
+ struct pollfd *pollfd = &fds[i];
+ // POLLHUP contradicts with POLLWRNORM.
+ if ((pollfd->revents & POLLHUP) != 0)
+ pollfd->revents &= ~POLLWRNORM;
+ if (pollfd->revents != 0)
+ ++retval;
+ }
+ return retval;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c b/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c
new file mode 100644
index 0000000..6100ea5
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sched/sched_yield.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <sched.h>
+
+int sched_yield(void) {
+ __wasi_errno_t error = __wasi_sched_yield();
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c
new file mode 100644
index 0000000..c1706db
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/stdio/renameat.c
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int __wasilibc_nocwd_renameat(int oldfd, const char *old, int newfd, const char *new) {
+ __wasi_errno_t error = __wasi_path_rename(oldfd, old, newfd, new);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c b/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c
new file mode 100644
index 0000000..5e266f0
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/stdlib/_Exit.c
@@ -0,0 +1,14 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <_/cdefs.h>
+#include <stdnoreturn.h>
+#include <unistd.h>
+
+noreturn void _Exit(int status) {
+ __wasi_proc_exit(status);
+}
+
+__strong_reference(_Exit, _exit);
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c b/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c
new file mode 100644
index 0000000..7d03cc6
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/ioctl/ioctl.c
@@ -0,0 +1,88 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/ioctl.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <stdarg.h>
+
+int ioctl(int fildes, int request, ...) {
+ switch (request) {
+ case FIONREAD: {
+ // Poll the file descriptor to determine how many bytes can be read.
+ __wasi_subscription_t subscriptions[2] = {
+ {
+ .u.tag = __WASI_EVENTTYPE_FD_READ,
+ .u.u.fd_read.file_descriptor = fildes,
+ },
+ {
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = __WASI_CLOCKID_MONOTONIC,
+ },
+ };
+ __wasi_event_t events[__arraycount(subscriptions)];
+ size_t nevents;
+ __wasi_errno_t error = __wasi_poll_oneoff(
+ subscriptions, events, __arraycount(subscriptions), &nevents);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Location where result should be written.
+ va_list ap;
+ va_start(ap, request);
+ int *result = va_arg(ap, int *);
+ va_end(ap);
+
+ // Extract number of bytes for reading from poll results.
+ for (size_t i = 0; i < nevents; ++i) {
+ __wasi_event_t *event = &events[i];
+ if (event->error != 0) {
+ errno = event->error;
+ return -1;
+ }
+ if (event->type == __WASI_EVENTTYPE_FD_READ) {
+ *result = event->fd_readwrite.nbytes;
+ return 0;
+ }
+ }
+
+ // No data available for reading.
+ *result = 0;
+ return 0;
+ }
+ case FIONBIO: {
+ // Obtain the current file descriptor flags.
+ __wasi_fdstat_t fds;
+ __wasi_errno_t error = __wasi_fd_fdstat_get(fildes, &fds);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Toggle the non-blocking flag based on the argument.
+ va_list ap;
+ va_start(ap, request);
+ if (*va_arg(ap, const int *) != 0)
+ fds.fs_flags |= __WASI_FDFLAGS_NONBLOCK;
+ else
+ fds.fs_flags &= ~__WASI_FDFLAGS_NONBLOCK;
+ va_end(ap);
+
+ // Update the file descriptor flags.
+ error = __wasi_fd_fdstat_set_flags(fildes, fds.fs_flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+ }
+ default:
+ // Invalid request.
+ errno = EINVAL;
+ return -1;
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c b/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c
new file mode 100644
index 0000000..fdc470e
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/select/pselect.c
@@ -0,0 +1,125 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/time.h>
+
+#include <sys/select.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+int pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
+ fd_set *restrict errorfds, const struct timespec *restrict timeout,
+ const sigset_t *sigmask) {
+ // Negative file descriptor upperbound.
+ if (nfds < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // This implementation does not support polling for exceptional
+ // conditions, such as out-of-band data on TCP sockets.
+ if (errorfds != NULL && errorfds->__nfds > 0) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ // Replace NULL pointers by the empty set.
+ fd_set empty;
+ FD_ZERO(&empty);
+ if (readfds == NULL)
+ readfds = &empty;
+ if (writefds == NULL)
+ writefds = &empty;
+
+ // Determine the maximum number of events.
+ size_t maxevents = readfds->__nfds + writefds->__nfds + 1;
+ __wasi_subscription_t subscriptions[maxevents];
+ size_t nsubscriptions = 0;
+
+ // Convert the readfds set.
+ for (size_t i = 0; i < readfds->__nfds; ++i) {
+ int fd = readfds->__fds[i];
+ if (fd < nfds) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = fd,
+ .u.tag = __WASI_EVENTTYPE_FD_READ,
+ .u.u.fd_read.file_descriptor = fd,
+ };
+ }
+ }
+
+ // Convert the writefds set.
+ for (size_t i = 0; i < writefds->__nfds; ++i) {
+ int fd = writefds->__fds[i];
+ if (fd < nfds) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .userdata = fd,
+ .u.tag = __WASI_EVENTTYPE_FD_WRITE,
+ .u.u.fd_write.file_descriptor = fd,
+ };
+ }
+ }
+
+ // Create extra event for the timeout.
+ if (timeout != NULL) {
+ __wasi_subscription_t *subscription = &subscriptions[nsubscriptions++];
+ *subscription = (__wasi_subscription_t){
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = __WASI_CLOCKID_REALTIME,
+ };
+ if (!timespec_to_timestamp_clamp(timeout, &subscription->u.u.clock.timeout)) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ // Execute poll().
+ size_t nevents;
+ __wasi_event_t events[nsubscriptions];
+ __wasi_errno_t error =
+ __wasi_poll_oneoff(subscriptions, events, nsubscriptions, &nevents);
+ if (error != 0) {
+ // WASI's poll requires at least one subscription, or else it returns
+ // `EINVAL`. Since a `pselect` with nothing to wait for is valid in POSIX,
+ // return `ENOTSUP` to indicate that we don't support that case.
+ //
+ // Wasm has no signal handling, so if none of the user-provided `pollfd`
+ // elements, nor the timeout, led us to producing even one subscription
+ // to wait for, there would be no way for the poll to wake up. WASI
+ // returns `EINVAL` in this case, but for users of `poll`, `ENOTSUP` is
+ // more likely to be understood.
+ if (nsubscriptions == 0)
+ errno = ENOTSUP;
+ else
+ errno = error;
+ return -1;
+ }
+
+ // Test for EBADF.
+ for (size_t i = 0; i < nevents; ++i) {
+ const __wasi_event_t *event = &events[i];
+ if ((event->type == __WASI_EVENTTYPE_FD_READ ||
+ event->type == __WASI_EVENTTYPE_FD_WRITE) &&
+ event->error == __WASI_ERRNO_BADF) {
+ errno = EBADF;
+ return -1;
+ }
+ }
+
+ // Clear and set entries in the result sets.
+ FD_ZERO(readfds);
+ FD_ZERO(writefds);
+ for (size_t i = 0; i < nevents; ++i) {
+ const __wasi_event_t *event = &events[i];
+ if (event->type == __WASI_EVENTTYPE_FD_READ) {
+ readfds->__fds[readfds->__nfds++] = event->userdata;
+ } else if (event->type == __WASI_EVENTTYPE_FD_WRITE) {
+ writefds->__fds[writefds->__nfds++] = event->userdata;
+ }
+ }
+ return readfds->__nfds + writefds->__nfds;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c b/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c
new file mode 100644
index 0000000..ebe5e8c
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/select/select.c
@@ -0,0 +1,25 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/select.h>
+
+#include <errno.h>
+#include <stddef.h>
+
+int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
+ fd_set *restrict errorfds, struct timeval *restrict timeout) {
+ if (timeout != NULL) {
+ // Timeout specified.
+ if (timeout->tv_usec < 0 || timeout->tv_usec >= 1000000) {
+ errno = EINVAL;
+ return -1;
+ }
+ struct timespec ts = {.tv_sec = timeout->tv_sec,
+ .tv_nsec = (long)timeout->tv_usec * 1000};
+ return pselect(nfds, readfds, writefds, errorfds, &ts, NULL);
+ } else {
+ // No timeout specified.
+ return pselect(nfds, readfds, writefds, errorfds, NULL, NULL);
+ }
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c
new file mode 100644
index 0000000..1fe41c4
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/getsockopt.c
@@ -0,0 +1,48 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+
+int getsockopt(int socket, int level, int option_name,
+ void *restrict option_value, socklen_t *restrict option_len) {
+ // Only support SOL_SOCKET options for now.
+ if (level != SOL_SOCKET) {
+ errno = ENOPROTOOPT;
+ return -1;
+ }
+
+ int value;
+ switch (option_name) {
+ case SO_TYPE: {
+ // Return the type of the socket. This information can simply be
+ // obtained by looking at the file descriptor type.
+ __wasi_fdstat_t fsb;
+ if (__wasi_fd_fdstat_get(socket, &fsb) != 0) {
+ errno = EBADF;
+ return -1;
+ }
+ if (fsb.fs_filetype != __WASI_FILETYPE_SOCKET_DGRAM &&
+ fsb.fs_filetype != __WASI_FILETYPE_SOCKET_STREAM) {
+ errno = ENOTSOCK;
+ return -1;
+ }
+ value = fsb.fs_filetype;
+ break;
+ }
+ default: {
+ errno = ENOPROTOOPT;
+ return -1;
+ }
+ }
+
+ // Copy out integer value.
+ memcpy(option_value, &value,
+ *option_len < sizeof(int) ? *option_len : sizeof(int));
+ *option_len = sizeof(int);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c
new file mode 100644
index 0000000..d35f889
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/recv.c
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <stdint.h>
+
+static_assert(MSG_PEEK == __WASI_RIFLAGS_RECV_PEEK, "Value mismatch");
+static_assert(MSG_WAITALL == __WASI_RIFLAGS_RECV_WAITALL, "Value mismatch");
+
+ssize_t recv(int socket, void *restrict buffer, size_t length, int flags) {
+ // Validate flags.
+ if ((flags & ~(MSG_PEEK | MSG_WAITALL)) != 0) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ // Prepare input parameters.
+ __wasi_iovec_t iov = {.buf = buffer, .buf_len = length};
+ __wasi_iovec_t *ri_data = &iov;
+ size_t ri_data_len = 1;
+ __wasi_riflags_t ri_flags = flags;
+
+ // Perform system call.
+ size_t ro_datalen;
+ __wasi_roflags_t ro_flags;
+ __wasi_errno_t error = __wasi_sock_recv(socket,
+ ri_data, ri_data_len, ri_flags,
+ &ro_datalen,
+ &ro_flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return ro_datalen;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c
new file mode 100644
index 0000000..85a298a
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/send.c
@@ -0,0 +1,32 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+
+ssize_t send(int socket, const void *buffer, size_t length, int flags) {
+ // This implementation does not support any flags.
+ if (flags != 0) {
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ // Prepare input parameters.
+ __wasi_ciovec_t iov = {.buf = buffer, .buf_len = length};
+ __wasi_ciovec_t *si_data = &iov;
+ size_t si_data_len = 1;
+ __wasi_siflags_t si_flags = 0;
+
+ // Perform system call.
+ size_t so_datalen;
+ __wasi_errno_t error = __wasi_sock_send(socket, si_data, si_data_len, si_flags, &so_datalen);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return so_datalen;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c b/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c
new file mode 100644
index 0000000..261fcb8
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/shutdown.c
@@ -0,0 +1,27 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+
+static_assert(SHUT_RD == __WASI_SDFLAGS_RD, "Value mismatch");
+static_assert(SHUT_WR == __WASI_SDFLAGS_WR, "Value mismatch");
+
+int shutdown(int socket, int how) {
+ // Validate shutdown flags.
+ if (how != SHUT_RD && how != SHUT_WR && how != SHUT_RDWR) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ __wasi_errno_t error = __wasi_sock_shutdown(socket, how);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return error;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h b/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h
new file mode 100644
index 0000000..7b1a366
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/socket/socket_impl.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef SYS_SOCKET_SOCKET_IMPL_H
+#define SYS_SOCKET_SOCKET_IMPL_H
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <stdalign.h>
+#include <stddef.h>
+#include <stdint.h>
+
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr),
+ "struct sockaddr alignment incorrect");
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr_in),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr_in),
+ "struct sockaddr_in alignment incorrect");
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr_in6),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr_in6),
+ "struct sockaddr_in6 alignment incorrect");
+static_assert(sizeof(struct sockaddr_storage) >= sizeof(struct sockaddr_un),
+ "struct sockaddr_storage too small");
+static_assert(alignof(struct sockaddr_storage) == alignof(struct sockaddr_un),
+ "struct sockaddr_un alignment incorrect");
+
+// Returns the control message header stored at a provided memory
+// address, ensuring that it is stored within the ancillary data buffer
+// of a message header.
+static inline struct cmsghdr *CMSG_GET(const struct msghdr *mhdr, void *cmsg) {
+ // Safety belt: require that the returned object is properly aligned.
+ assert((uintptr_t)cmsg % alignof(struct cmsghdr) == 0 &&
+ "Attempted to access unaligned control message header");
+
+ // Safety belt: the computed starting address of the control message
+ // header may only lie inside the ancillary data buffer, or right
+ // after it in case we've reached the end of the buffer.
+ const unsigned char *begin = mhdr->msg_control;
+ const unsigned char *end = begin + mhdr->msg_controllen;
+ assert((unsigned char *)cmsg >= begin &&
+ (unsigned char *)cmsg < end + alignof(struct cmsghdr) &&
+ "Computed object outside of buffer boundaries");
+
+ // Only return the control message header in case all of its fields
+ // lie within the ancillary data buffer.
+ return CMSG_DATA((struct cmsghdr *)cmsg) <= end ? cmsg : NULL;
+}
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c
new file mode 100644
index 0000000..b8ffdb5
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstat.c
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+#include "stat_impl.h"
+
+int fstat(int fildes, struct stat *buf) {
+ __wasi_filestat_t internal_stat;
+ __wasi_errno_t error = __wasi_fd_filestat_get(fildes, &internal_stat);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ to_public_stat(&internal_stat, buf);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c
new file mode 100644
index 0000000..25b29ac
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/fstatat.c
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "stat_impl.h"
+
+int __wasilibc_nocwd_fstatat(int fd, const char *restrict path, struct stat *restrict buf,
+ int flag) {
+ // Create lookup properties.
+ __wasi_lookupflags_t lookup_flags = 0;
+ if ((flag & AT_SYMLINK_NOFOLLOW) == 0)
+ lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Perform system call.
+ __wasi_filestat_t internal_stat;
+ __wasi_errno_t error =
+ __wasi_path_filestat_get(fd, lookup_flags, path, &internal_stat);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ to_public_stat(&internal_stat, buf);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c
new file mode 100644
index 0000000..13357fc
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/futimens.c
@@ -0,0 +1,29 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+#include "stat_impl.h"
+
+int futimens(int fd, const struct timespec *times) {
+ // Convert timestamps and extract NOW/OMIT flags.
+ __wasi_timestamp_t st_atim;
+ __wasi_timestamp_t st_mtim;
+ __wasi_fstflags_t flags;
+ if (!utimens_get_timestamps(times, &st_atim, &st_mtim, &flags)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Perform system call.
+ __wasi_errno_t error = __wasi_fd_filestat_set_times(fd, st_atim, st_mtim, flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c
new file mode 100644
index 0000000..fd27d5e
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/mkdirat.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+
+int __wasilibc_nocwd_mkdirat_nomode(int fd, const char *path) {
+ __wasi_errno_t error = __wasi_path_create_directory(fd, path);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h b/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h
new file mode 100644
index 0000000..9726b51
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/stat_impl.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2015-2017 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#ifndef SYS_STAT_STAT_IMPL_H
+#define SYS_STAT_STAT_IMPL_H
+
+#include <common/time.h>
+
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <stdbool.h>
+
+static_assert(S_ISBLK(S_IFBLK), "Value mismatch");
+static_assert(S_ISCHR(S_IFCHR), "Value mismatch");
+static_assert(S_ISDIR(S_IFDIR), "Value mismatch");
+static_assert(S_ISFIFO(S_IFIFO), "Value mismatch");
+static_assert(S_ISLNK(S_IFLNK), "Value mismatch");
+static_assert(S_ISREG(S_IFREG), "Value mismatch");
+static_assert(S_ISSOCK(S_IFSOCK), "Value mismatch");
+
+static inline void to_public_stat(const __wasi_filestat_t *in,
+ struct stat *out) {
+ // Ensure that we don't truncate any values.
+ static_assert(sizeof(in->dev) == sizeof(out->st_dev), "Size mismatch");
+ static_assert(sizeof(in->ino) == sizeof(out->st_ino), "Size mismatch");
+ /*
+ * The non-standard __st_filetype field appears to only be used for shared
+ * memory, which we don't currently support.
+ */
+ /* nlink_t is 64-bit on wasm32, following the x32 ABI. */
+ static_assert(sizeof(in->nlink) <= sizeof(out->st_nlink), "Size shortfall");
+ static_assert(sizeof(in->size) == sizeof(out->st_size), "Size mismatch");
+
+ *out = (struct stat){
+ .st_dev = in->dev,
+ .st_ino = in->ino,
+ .st_nlink = in->nlink,
+ .st_size = in->size,
+ .st_atim = timestamp_to_timespec(in->atim),
+ .st_mtim = timestamp_to_timespec(in->mtim),
+ .st_ctim = timestamp_to_timespec(in->ctim),
+ };
+
+ // Convert file type to legacy types encoded in st_mode.
+ switch (in->filetype) {
+ case __WASI_FILETYPE_BLOCK_DEVICE:
+ out->st_mode |= S_IFBLK;
+ break;
+ case __WASI_FILETYPE_CHARACTER_DEVICE:
+ out->st_mode |= S_IFCHR;
+ break;
+ case __WASI_FILETYPE_DIRECTORY:
+ out->st_mode |= S_IFDIR;
+ break;
+ case __WASI_FILETYPE_REGULAR_FILE:
+ out->st_mode |= S_IFREG;
+ break;
+ case __WASI_FILETYPE_SOCKET_DGRAM:
+ case __WASI_FILETYPE_SOCKET_STREAM:
+ out->st_mode |= S_IFSOCK;
+ break;
+ case __WASI_FILETYPE_SYMBOLIC_LINK:
+ out->st_mode |= S_IFLNK;
+ break;
+ }
+}
+
+static inline bool utimens_get_timestamps(const struct timespec *times,
+ __wasi_timestamp_t *st_atim,
+ __wasi_timestamp_t *st_mtim,
+ __wasi_fstflags_t *flags) {
+ if (times == NULL) {
+ // Update both timestamps.
+ *flags = __WASI_FSTFLAGS_ATIM_NOW | __WASI_FSTFLAGS_MTIM_NOW;
+ *st_atim = (__wasi_timestamp_t) { 0 };
+ *st_mtim = (__wasi_timestamp_t) { 0 };
+ } else {
+ // Set individual timestamps.
+ *flags = 0;
+ switch (times[0].tv_nsec) {
+ case UTIME_NOW:
+ *flags |= __WASI_FSTFLAGS_ATIM_NOW;
+ *st_atim = (__wasi_timestamp_t) { 0 };
+ break;
+ case UTIME_OMIT:
+ *st_atim = (__wasi_timestamp_t) { 0 };
+ break;
+ default:
+ *flags |= __WASI_FSTFLAGS_ATIM;
+ if (!timespec_to_timestamp_exact(&times[0], st_atim))
+ return false;
+ break;
+ }
+
+ switch (times[1].tv_nsec) {
+ case UTIME_NOW:
+ *flags |= __WASI_FSTFLAGS_MTIM_NOW;
+ *st_mtim = (__wasi_timestamp_t) { 0 };
+ break;
+ case UTIME_OMIT:
+ *st_mtim = (__wasi_timestamp_t) { 0 };
+ break;
+ default:
+ *flags |= __WASI_FSTFLAGS_MTIM;
+ if (!timespec_to_timestamp_exact(&times[1], st_mtim))
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+
+#endif
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c
new file mode 100644
index 0000000..19508a1
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/stat/utimensat.c
@@ -0,0 +1,38 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/stat.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "stat_impl.h"
+
+int __wasilibc_nocwd_utimensat(int fd, const char *path, const struct timespec times[2],
+ int flag) {
+ // Convert timestamps and extract NOW/OMIT flags.
+ __wasi_timestamp_t st_atim;
+ __wasi_timestamp_t st_mtim;
+ __wasi_fstflags_t flags;
+ if (!utimens_get_timestamps(times, &st_atim, &st_mtim, &flags)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Create lookup properties.
+ __wasi_lookupflags_t lookup_flags = 0;
+ if ((flag & AT_SYMLINK_NOFOLLOW) == 0)
+ lookup_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Perform system call.
+ __wasi_errno_t error =
+ __wasi_path_filestat_set_times(fd, lookup_flags, path, st_atim, st_mtim, flags);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c b/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c
new file mode 100644
index 0000000..596bdff
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/time/gettimeofday.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/time.h>
+
+#include <sys/time.h>
+
+#include <wasi/api.h>
+
+int gettimeofday(struct timeval *restrict tp, void *tz) {
+ if (tp != NULL) {
+ __wasi_timestamp_t ts = 0;
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_REALTIME, 1000, &ts);
+ *tp = timestamp_to_timeval(ts);
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c
new file mode 100644
index 0000000..36f882d
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/preadv.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+ssize_t preadv(int fildes, const struct iovec *iov, int iovcnt, off_t offset) {
+ if (iovcnt < 0 || offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_read;
+ __wasi_errno_t error = __wasi_fd_pread(
+ fildes, (const __wasi_iovec_t *)iov, iovcnt, offset, &bytes_read);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c
new file mode 100644
index 0000000..d6f8851
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/pwritev.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+
+ssize_t pwritev(int fildes, const struct iovec *iov, int iovcnt, off_t offset) {
+ if (iovcnt < 0 || offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_written;
+ __wasi_errno_t error = __wasi_fd_pwrite(
+ fildes, (const __wasi_ciovec_t *)iov, iovcnt, offset, &bytes_written);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c
new file mode 100644
index 0000000..e3eaebe
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/readv.c
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <stddef.h>
+
+static_assert(offsetof(struct iovec, iov_base) ==
+ offsetof(__wasi_iovec_t, buf),
+ "Offset mismatch");
+static_assert(sizeof(((struct iovec *)0)->iov_base) ==
+ sizeof(((__wasi_iovec_t *)0)->buf),
+ "Size mismatch");
+static_assert(offsetof(struct iovec, iov_len) ==
+ offsetof(__wasi_iovec_t, buf_len),
+ "Offset mismatch");
+static_assert(sizeof(((struct iovec *)0)->iov_len) ==
+ sizeof(((__wasi_iovec_t *)0)->buf_len),
+ "Size mismatch");
+static_assert(sizeof(struct iovec) == sizeof(__wasi_iovec_t),
+ "Size mismatch");
+
+ssize_t readv(int fildes, const struct iovec *iov, int iovcnt) {
+ if (iovcnt < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_read;
+ __wasi_errno_t error = __wasi_fd_read(
+ fildes, (const __wasi_iovec_t *)iov, iovcnt, &bytes_read);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c b/libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c
new file mode 100644
index 0000000..459b152
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/sys/uio/writev.c
@@ -0,0 +1,40 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <stddef.h>
+
+static_assert(offsetof(struct iovec, iov_base) ==
+ offsetof(__wasi_ciovec_t, buf),
+ "Offset mismatch");
+static_assert(sizeof(((struct iovec *)0)->iov_base) ==
+ sizeof(((__wasi_ciovec_t *)0)->buf),
+ "Size mismatch");
+static_assert(offsetof(struct iovec, iov_len) ==
+ offsetof(__wasi_ciovec_t, buf_len),
+ "Offset mismatch");
+static_assert(sizeof(((struct iovec *)0)->iov_len) ==
+ sizeof(((__wasi_ciovec_t *)0)->buf_len),
+ "Size mismatch");
+static_assert(sizeof(struct iovec) == sizeof(__wasi_ciovec_t),
+ "Size mismatch");
+
+ssize_t writev(int fildes, const struct iovec *iov, int iovcnt) {
+ if (iovcnt < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ size_t bytes_written;
+ __wasi_errno_t error = __wasi_fd_write(
+ fildes, (const __wasi_ciovec_t *)iov, iovcnt, &bytes_written);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c
new file mode 100644
index 0000000..a4c4a62
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_MONOTONIC.c
@@ -0,0 +1,12 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+
+#include <wasi/api.h>
+#include <time.h>
+
+const struct __clockid _CLOCK_MONOTONIC = {
+ .id = __WASI_CLOCKID_MONOTONIC,
+};
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c
new file mode 100644
index 0000000..9523754
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/CLOCK_REALTIME.c
@@ -0,0 +1,12 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+
+#include <wasi/api.h>
+#include <time.h>
+
+const struct __clockid _CLOCK_REALTIME = {
+ .id = __WASI_CLOCKID_REALTIME,
+};
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c
new file mode 100644
index 0000000..8030d4b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_getres.c
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+#include <common/time.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <time.h>
+
+int clock_getres(clockid_t clock_id, struct timespec *res) {
+ __wasi_timestamp_t ts;
+ __wasi_errno_t error = __wasi_clock_res_get(clock_id->id, &ts);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ *res = timestamp_to_timespec(ts);
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c
new file mode 100644
index 0000000..c7e1a60
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_gettime.c
@@ -0,0 +1,22 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+#include <common/time.h>
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <time.h>
+
+int __clock_gettime(clockid_t clock_id, struct timespec *tp) {
+ __wasi_timestamp_t ts;
+ __wasi_errno_t error = __wasi_clock_time_get(clock_id->id, 1, &ts);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ *tp = timestamp_to_timespec(ts);
+ return 0;
+}
+weak_alias(__clock_gettime, clock_gettime);
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c b/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c
new file mode 100644
index 0000000..d375056
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/clock_nanosleep.c
@@ -0,0 +1,37 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/clock.h>
+#include <common/time.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <time.h>
+
+static_assert(TIMER_ABSTIME == __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME,
+ "Value mismatch");
+
+int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *rqtp,
+ struct timespec *rmtp) {
+ if ((flags & ~TIMER_ABSTIME) != 0)
+ return EINVAL;
+
+ // Prepare polling subscription.
+ __wasi_subscription_t sub = {
+ .u.tag = __WASI_EVENTTYPE_CLOCK,
+ .u.u.clock.id = clock_id->id,
+ .u.u.clock.flags = flags,
+ };
+ if (!timespec_to_timestamp_clamp(rqtp, &sub.u.u.clock.timeout))
+ return EINVAL;
+
+ // Block until polling event is triggered.
+ size_t nevents;
+ __wasi_event_t ev;
+ __wasi_errno_t error = __wasi_poll_oneoff(&sub, &ev, 1, &nevents);
+ return error == 0 && ev.error == 0 ? 0 : ENOTSUP;
+}
+
+weak_alias(clock_nanosleep, __clock_nanosleep);
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c b/libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c
new file mode 100644
index 0000000..5f26c5c
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/nanosleep.c
@@ -0,0 +1,15 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <errno.h>
+#include <time.h>
+
+int nanosleep(const struct timespec *rqtp, struct timespec *rem) {
+ int error = clock_nanosleep(CLOCK_REALTIME, 0, rqtp, rem);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/time/time.c b/libc-bottom-half/cloudlibc/src/libc/time/time.c
new file mode 100644
index 0000000..52bc0e4
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/time/time.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <common/time.h>
+
+#include <wasi/api.h>
+#include <time.h>
+
+time_t time(time_t *tloc) {
+ __wasi_timestamp_t ts = 0;
+ (void)__wasi_clock_time_get(__WASI_CLOCKID_REALTIME, NSEC_PER_SEC, &ts);
+ if (tloc != NULL)
+ *tloc = ts / NSEC_PER_SEC;
+ return ts / NSEC_PER_SEC;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/close.c b/libc-bottom-half/cloudlibc/src/libc/unistd/close.c
new file mode 100644
index 0000000..2f5814b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/close.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int close(int fildes) {
+ __wasi_errno_t error = __wasi_fd_close(fildes);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c
new file mode 100644
index 0000000..ffaef6e
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/faccessat.c
@@ -0,0 +1,53 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int __wasilibc_nocwd_faccessat(int fd, const char *path, int amode, int flag) {
+ // Validate function parameters.
+ if ((amode & ~(F_OK | R_OK | W_OK | X_OK)) != 0 ||
+ (flag & ~AT_EACCESS) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Check for target file existence and obtain the file type.
+ __wasi_lookupflags_t lookup_flags = __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+ __wasi_filestat_t file;
+ __wasi_errno_t error =
+ __wasi_path_filestat_get(fd, lookup_flags, path, &file);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Test whether the requested access rights are present on the
+ // directory file descriptor.
+ if (amode != 0) {
+ __wasi_fdstat_t directory;
+ error = __wasi_fd_fdstat_get(fd, &directory);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ __wasi_rights_t min = 0;
+ if ((amode & R_OK) != 0)
+ min |= file.filetype == __WASI_FILETYPE_DIRECTORY
+ ? __WASI_RIGHTS_FD_READDIR
+ : __WASI_RIGHTS_FD_READ;
+ if ((amode & W_OK) != 0)
+ min |= __WASI_RIGHTS_FD_WRITE;
+
+ if ((min & directory.fs_rights_inheriting) != min) {
+ errno = EACCES;
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c
new file mode 100644
index 0000000..b821b06
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fdatasync.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int fdatasync(int fildes) {
+ __wasi_errno_t error = __wasi_fd_datasync(fildes);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EBADF : error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c
new file mode 100644
index 0000000..733e294
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/fsync.c
@@ -0,0 +1,16 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int fsync(int fildes) {
+ __wasi_errno_t error = __wasi_fd_sync(fildes);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EINVAL : error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c
new file mode 100644
index 0000000..7792597
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/ftruncate.c
@@ -0,0 +1,22 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+int ftruncate(int fildes, off_t length) {
+ if (length < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ __wasi_filesize_t st_size = length;
+ __wasi_errno_t error =
+ __wasi_fd_filestat_set_size(fildes, st_size);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c
new file mode 100644
index 0000000..d57f562
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/linkat.c
@@ -0,0 +1,24 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int __wasilibc_nocwd_linkat(int fd1, const char *path1, int fd2, const char *path2, int flag) {
+ // Create lookup properties.
+ __wasi_lookupflags_t lookup1_flags = 0;
+ if ((flag & AT_SYMLINK_FOLLOW) != 0)
+ lookup1_flags |= __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
+
+ // Perform system call.
+ __wasi_errno_t error = __wasi_path_link(fd1, lookup1_flags, path1, fd2, path2);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c
new file mode 100644
index 0000000..3e0429f
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/lseek.c
@@ -0,0 +1,25 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+static_assert(SEEK_CUR == __WASI_WHENCE_CUR, "Value mismatch");
+static_assert(SEEK_END == __WASI_WHENCE_END, "Value mismatch");
+static_assert(SEEK_SET == __WASI_WHENCE_SET, "Value mismatch");
+
+off_t __lseek(int fildes, off_t offset, int whence) {
+ __wasi_filesize_t new_offset;
+ __wasi_errno_t error =
+ __wasi_fd_seek(fildes, offset, whence, &new_offset);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? ESPIPE : error;
+ return -1;
+ }
+ return new_offset;
+}
+
+weak_alias(__lseek, lseek);
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c
new file mode 100644
index 0000000..c9944bc
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pread.c
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset) {
+ if (offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ __wasi_iovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_read;
+ __wasi_errno_t error =
+ __wasi_fd_pread(fildes, &iov, 1, offset, &bytes_read);
+ if (error != 0) {
+ __wasi_fdstat_t fds;
+ if (error == ENOTCAPABLE && __wasi_fd_fdstat_get(fildes, &fds) == 0) {
+ // Determine why we got ENOTCAPABLE.
+ if ((fds.fs_rights_base & __WASI_RIGHTS_FD_READ) == 0)
+ error = EBADF;
+ else
+ error = ESPIPE;
+ }
+ errno = error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c
new file mode 100644
index 0000000..f80bc40
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/pwrite.c
@@ -0,0 +1,31 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset) {
+ if (offset < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ __wasi_ciovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_written;
+ __wasi_errno_t error =
+ __wasi_fd_pwrite(fildes, &iov, 1, offset, &bytes_written);
+ if (error != 0) {
+ __wasi_fdstat_t fds;
+ if (error == ENOTCAPABLE && __wasi_fd_fdstat_get(fildes, &fds) == 0) {
+ // Determine why we got ENOTCAPABLE.
+ if ((fds.fs_rights_base & __WASI_RIGHTS_FD_WRITE) == 0)
+ error = EBADF;
+ else
+ error = ESPIPE;
+ }
+ errno = error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/read.c b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c
new file mode 100644
index 0000000..1582126
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/read.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t read(int fildes, void *buf, size_t nbyte) {
+ __wasi_iovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_read;
+ __wasi_errno_t error = __wasi_fd_read(fildes, &iov, 1, &bytes_read);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EBADF : error;
+ return -1;
+ }
+ return bytes_read;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c
new file mode 100644
index 0000000..7a3bce2
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/readlinkat.c
@@ -0,0 +1,21 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+ssize_t __wasilibc_nocwd_readlinkat(int fd, const char *restrict path, char *restrict buf,
+ size_t bufsize) {
+ size_t bufused;
+ // TODO: Remove the cast on `buf` once the witx is updated with char8 support.
+ __wasi_errno_t error = __wasi_path_readlink(fd, path,
+ (uint8_t*)buf, bufsize, &bufused);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return bufused;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c b/libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c
new file mode 100644
index 0000000..970287b
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/sleep.c
@@ -0,0 +1,13 @@
+// Copyright (c) 2015 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <time.h>
+#include <unistd.h>
+
+unsigned int sleep(unsigned int seconds) {
+ struct timespec ts = {.tv_sec = seconds, .tv_nsec = 0};
+ if (clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL) != 0)
+ return seconds;
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c
new file mode 100644
index 0000000..0aa38be
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/symlinkat.c
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+int __wasilibc_nocwd_symlinkat(const char *path1, int fd, const char *path2) {
+ __wasi_errno_t error = __wasi_path_symlink(path1, fd, path2);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c b/libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c
new file mode 100644
index 0000000..351bf92
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/unlinkat.c
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+int unlinkat(int fd, const char *path, int flag) {
+ if ((flag & AT_REMOVEDIR) != 0) {
+ return __wasilibc_rmdirat(fd, path);
+ }
+ return __wasilibc_unlinkat(fd, path);
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c b/libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c
new file mode 100644
index 0000000..8005f17
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/usleep.c
@@ -0,0 +1,18 @@
+// Copyright (c) 2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+int usleep(useconds_t useconds) {
+ struct timespec ts = {.tv_sec = useconds / 1000000,
+ .tv_nsec = useconds % 1000000 * 1000};
+ int error = clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/write.c b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c
new file mode 100644
index 0000000..a6567e8
--- /dev/null
+++ b/libc-bottom-half/cloudlibc/src/libc/unistd/write.c
@@ -0,0 +1,19 @@
+// Copyright (c) 2015-2016 Nuxi, https://nuxi.nl/
+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <wasi/api.h>
+#include <errno.h>
+#include <unistd.h>
+
+ssize_t write(int fildes, const void *buf, size_t nbyte) {
+ __wasi_ciovec_t iov = {.buf = buf, .buf_len = nbyte};
+ size_t bytes_written;
+ __wasi_errno_t error =
+ __wasi_fd_write(fildes, &iov, 1, &bytes_written);
+ if (error != 0) {
+ errno = error == ENOTCAPABLE ? EBADF : error;
+ return -1;
+ }
+ return bytes_written;
+}
diff --git a/libc-bottom-half/crt/crt1-command.c b/libc-bottom-half/crt/crt1-command.c
new file mode 100644
index 0000000..fb9ee71
--- /dev/null
+++ b/libc-bottom-half/crt/crt1-command.c
@@ -0,0 +1,53 @@
+#ifdef _REENTRANT
+#include <stdatomic.h>
+extern void __wasi_init_tp(void);
+#endif
+#include <wasi/api.h>
+extern void __wasm_call_ctors(void);
+extern int __main_void(void);
+extern void __wasm_call_dtors(void);
+
+__attribute__((export_name("_start")))
+void _start(void) {
+ // Commands should only be called once per instance. This simple check
+ // ensures that the `_start` function isn't started more than once.
+ //
+ // We use `volatile` here to prevent the store to `started` from being
+ // sunk past any subsequent code, and to prevent any compiler from
+ // optimizing based on the knowledge that `_start` is the program
+ // entrypoint.
+#ifdef _REENTRANT
+ static volatile _Atomic int started = 0;
+ int expected = 0;
+ if (!atomic_compare_exchange_strong(&started, &expected, 1)) {
+ __builtin_trap();
+ }
+#else
+ static volatile int started = 0;
+ if (started != 0) {
+ __builtin_trap();
+ }
+ started = 1;
+#endif
+
+#ifdef _REENTRANT
+ __wasi_init_tp();
+#endif
+
+ // The linker synthesizes this to call constructors.
+ __wasm_call_ctors();
+
+ // Call `__main_void` which will either be the application's zero-argument
+ // `__main_void` function or a libc routine which obtains the command-line
+ // arguments and calls `__main_argv_argc`.
+ int r = __main_void();
+
+ // Call atexit functions, destructors, stdio cleanup, etc.
+ __wasm_call_dtors();
+
+ // If main exited successfully, just return, otherwise call
+ // `__wasi_proc_exit`.
+ if (r != 0) {
+ __wasi_proc_exit(r);
+ }
+}
diff --git a/libc-bottom-half/crt/crt1-reactor.c b/libc-bottom-half/crt/crt1-reactor.c
new file mode 100644
index 0000000..f507c9e
--- /dev/null
+++ b/libc-bottom-half/crt/crt1-reactor.c
@@ -0,0 +1,7 @@
+extern void __wasm_call_ctors(void);
+
+__attribute__((export_name("_initialize")))
+void _initialize(void) {
+ // The linker synthesizes this to call constructors.
+ __wasm_call_ctors();
+}
diff --git a/libc-bottom-half/crt/crt1.c b/libc-bottom-half/crt/crt1.c
new file mode 100644
index 0000000..cbbe073
--- /dev/null
+++ b/libc-bottom-half/crt/crt1.c
@@ -0,0 +1,3 @@
+// We compile a plain crt1.o for toolchain compatibility, but it's
+// identical to crt1-command.o.
+#include "crt1-command.c"
diff --git a/libc-bottom-half/getpid/getpid.c b/libc-bottom-half/getpid/getpid.c
new file mode 100644
index 0000000..d2920ac
--- /dev/null
+++ b/libc-bottom-half/getpid/getpid.c
@@ -0,0 +1,6 @@
+#include <unistd.h>
+
+pid_t getpid(void) {
+ // Return an arbitrary value, greater than 1 which is special.
+ return 42;
+}
diff --git a/libc-bottom-half/headers/private/_/limits.h b/libc-bottom-half/headers/private/_/limits.h
new file mode 100644
index 0000000..1e189a1
--- /dev/null
+++ b/libc-bottom-half/headers/private/_/limits.h
@@ -0,0 +1 @@
+#include <limits.h>
diff --git a/libc-bottom-half/headers/private/_/struct/timespec.h b/libc-bottom-half/headers/private/_/struct/timespec.h
new file mode 100644
index 0000000..fbe602a
--- /dev/null
+++ b/libc-bottom-half/headers/private/_/struct/timespec.h
@@ -0,0 +1 @@
+#include <__struct_timespec.h>
diff --git a/libc-bottom-half/headers/private/_/struct/timeval.h b/libc-bottom-half/headers/private/_/struct/timeval.h
new file mode 100644
index 0000000..9cabfd7
--- /dev/null
+++ b/libc-bottom-half/headers/private/_/struct/timeval.h
@@ -0,0 +1 @@
+#include <__struct_timeval.h>
diff --git a/libc-bottom-half/headers/private/_/types.h b/libc-bottom-half/headers/private/_/types.h
new file mode 100644
index 0000000..42b17e5
--- /dev/null
+++ b/libc-bottom-half/headers/private/_/types.h
@@ -0,0 +1,9 @@
+#include <_/cdefs.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <sys/types.h>
+
+typedef uint64_t __uint64_t;
+#define _UINT64_C UINT64_C
diff --git a/libc-bottom-half/headers/private/assert.h b/libc-bottom-half/headers/private/assert.h
new file mode 100644
index 0000000..8e19ff4
--- /dev/null
+++ b/libc-bottom-half/headers/private/assert.h
@@ -0,0 +1,7 @@
+#include <stdlib.h>
+
+#ifndef __cplusplus
+#define static_assert _Static_assert
+#endif
+
+#define assert(x) ((void)((x) || (abort(), 0)))
diff --git a/libc-bottom-half/headers/private/common/crt.h b/libc-bottom-half/headers/private/common/crt.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libc-bottom-half/headers/private/common/crt.h
diff --git a/libc-bottom-half/headers/private/errno.h b/libc-bottom-half/headers/private/errno.h
new file mode 100644
index 0000000..3b37313
--- /dev/null
+++ b/libc-bottom-half/headers/private/errno.h
@@ -0,0 +1,2 @@
+#include_next <errno.h>
+#define EOPNOTSUPP ENOTSUP
diff --git a/libc-bottom-half/headers/private/fcntl.h b/libc-bottom-half/headers/private/fcntl.h
new file mode 100644
index 0000000..3210418
--- /dev/null
+++ b/libc-bottom-half/headers/private/fcntl.h
@@ -0,0 +1,2 @@
+#include_next <fcntl.h>
+#include <_/types.h>
diff --git a/libc-bottom-half/headers/private/sched.h b/libc-bottom-half/headers/private/sched.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libc-bottom-half/headers/private/sched.h
diff --git a/libc-bottom-half/headers/private/stdarg.h b/libc-bottom-half/headers/private/stdarg.h
new file mode 100644
index 0000000..e019608
--- /dev/null
+++ b/libc-bottom-half/headers/private/stdarg.h
@@ -0,0 +1,2 @@
+#include_next <stdarg.h>
+#include <_/cdefs.h>
diff --git a/libc-bottom-half/headers/private/stdint.h b/libc-bottom-half/headers/private/stdint.h
new file mode 100644
index 0000000..1fed338
--- /dev/null
+++ b/libc-bottom-half/headers/private/stdint.h
@@ -0,0 +1,12 @@
+#include_next <stdint.h>
+
+#define __need_size_t
+#include <stddef.h>
+
+#include <__typedef_clock_t.h>
+#include <__typedef_time_t.h>
+#include <__typedef_blksize_t.h>
+#include <__typedef_off_t.h>
+#include <__typedef_ssize_t.h>
+#include <__typedef_suseconds_t.h>
+#include <__typedef_nlink_t.h>
diff --git a/libc-bottom-half/headers/private/stdio.h b/libc-bottom-half/headers/private/stdio.h
new file mode 100644
index 0000000..b135b6a
--- /dev/null
+++ b/libc-bottom-half/headers/private/stdio.h
@@ -0,0 +1,3 @@
+#include <_/cdefs.h>
+int snprintf(char *str, size_t size, const char *format, ...);
+int rename(const char *oldpath, const char *newpath);
diff --git a/libc-bottom-half/headers/private/stdlib.h b/libc-bottom-half/headers/private/stdlib.h
new file mode 100644
index 0000000..8c2b395
--- /dev/null
+++ b/libc-bottom-half/headers/private/stdlib.h
@@ -0,0 +1,8 @@
+#define __need_size_t
+#define __need_wchar_t
+#define __need_NULL
+#include <stddef.h>
+
+#include_next <stdlib.h>
+
+int clearenv(void);
diff --git a/libc-bottom-half/headers/private/string.h b/libc-bottom-half/headers/private/string.h
new file mode 100644
index 0000000..1c4ce19
--- /dev/null
+++ b/libc-bottom-half/headers/private/string.h
@@ -0,0 +1,2 @@
+#include_next <string.h>
+#include <_/cdefs.h>
diff --git a/libc-bottom-half/headers/private/sys/mman.h b/libc-bottom-half/headers/private/sys/mman.h
new file mode 100644
index 0000000..bdd5401
--- /dev/null
+++ b/libc-bottom-half/headers/private/sys/mman.h
@@ -0,0 +1,2 @@
+#include_next <sys/mman.h>
+#include <_/types.h>
diff --git a/libc-bottom-half/headers/private/threads.h b/libc-bottom-half/headers/private/threads.h
new file mode 100644
index 0000000..b51ef0d
--- /dev/null
+++ b/libc-bottom-half/headers/private/threads.h
@@ -0,0 +1,3 @@
+#ifndef __cplusplus
+#define thread_local _Thread_local
+#endif
diff --git a/libc-bottom-half/headers/public/__errno.h b/libc-bottom-half/headers/public/__errno.h
new file mode 100644
index 0000000..4fd983a
--- /dev/null
+++ b/libc-bottom-half/headers/public/__errno.h
@@ -0,0 +1,20 @@
+#ifndef __wasilibc___errno_h
+#define __wasilibc___errno_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+extern thread_local int errno;
+#else
+extern _Thread_local int errno;
+#endif
+
+#define errno errno
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__errno_values.h b/libc-bottom-half/headers/public/__errno_values.h
new file mode 100644
index 0000000..6d6d412
--- /dev/null
+++ b/libc-bottom-half/headers/public/__errno_values.h
@@ -0,0 +1,86 @@
+#ifndef __wasilibc___errno_values_h
+#define __wasilibc___errno_values_h
+
+#include <wasi/api.h>
+
+#define E2BIG __WASI_ERRNO_2BIG
+#define EACCES __WASI_ERRNO_ACCES
+#define EADDRINUSE __WASI_ERRNO_ADDRINUSE
+#define EADDRNOTAVAIL __WASI_ERRNO_ADDRNOTAVAIL
+#define EAFNOSUPPORT __WASI_ERRNO_AFNOSUPPORT
+#define EAGAIN __WASI_ERRNO_AGAIN
+#define EALREADY __WASI_ERRNO_ALREADY
+#define EBADF __WASI_ERRNO_BADF
+#define EBADMSG __WASI_ERRNO_BADMSG
+#define EBUSY __WASI_ERRNO_BUSY
+#define ECANCELED __WASI_ERRNO_CANCELED
+#define ECHILD __WASI_ERRNO_CHILD
+#define ECONNABORTED __WASI_ERRNO_CONNABORTED
+#define ECONNREFUSED __WASI_ERRNO_CONNREFUSED
+#define ECONNRESET __WASI_ERRNO_CONNRESET
+#define EDEADLK __WASI_ERRNO_DEADLK
+#define EDESTADDRREQ __WASI_ERRNO_DESTADDRREQ
+#define EDOM __WASI_ERRNO_DOM
+#define EDQUOT __WASI_ERRNO_DQUOT
+#define EEXIST __WASI_ERRNO_EXIST
+#define EFAULT __WASI_ERRNO_FAULT
+#define EFBIG __WASI_ERRNO_FBIG
+#define EHOSTUNREACH __WASI_ERRNO_HOSTUNREACH
+#define EIDRM __WASI_ERRNO_IDRM
+#define EILSEQ __WASI_ERRNO_ILSEQ
+#define EINPROGRESS __WASI_ERRNO_INPROGRESS
+#define EINTR __WASI_ERRNO_INTR
+#define EINVAL __WASI_ERRNO_INVAL
+#define EIO __WASI_ERRNO_IO
+#define EISCONN __WASI_ERRNO_ISCONN
+#define EISDIR __WASI_ERRNO_ISDIR
+#define ELOOP __WASI_ERRNO_LOOP
+#define EMFILE __WASI_ERRNO_MFILE
+#define EMLINK __WASI_ERRNO_MLINK
+#define EMSGSIZE __WASI_ERRNO_MSGSIZE
+#define EMULTIHOP __WASI_ERRNO_MULTIHOP
+#define ENAMETOOLONG __WASI_ERRNO_NAMETOOLONG
+#define ENETDOWN __WASI_ERRNO_NETDOWN
+#define ENETRESET __WASI_ERRNO_NETRESET
+#define ENETUNREACH __WASI_ERRNO_NETUNREACH
+#define ENFILE __WASI_ERRNO_NFILE
+#define ENOBUFS __WASI_ERRNO_NOBUFS
+#define ENODEV __WASI_ERRNO_NODEV
+#define ENOENT __WASI_ERRNO_NOENT
+#define ENOEXEC __WASI_ERRNO_NOEXEC
+#define ENOLCK __WASI_ERRNO_NOLCK
+#define ENOLINK __WASI_ERRNO_NOLINK
+#define ENOMEM __WASI_ERRNO_NOMEM
+#define ENOMSG __WASI_ERRNO_NOMSG
+#define ENOPROTOOPT __WASI_ERRNO_NOPROTOOPT
+#define ENOSPC __WASI_ERRNO_NOSPC
+#define ENOSYS __WASI_ERRNO_NOSYS
+#define ENOTCONN __WASI_ERRNO_NOTCONN
+#define ENOTDIR __WASI_ERRNO_NOTDIR
+#define ENOTEMPTY __WASI_ERRNO_NOTEMPTY
+#define ENOTRECOVERABLE __WASI_ERRNO_NOTRECOVERABLE
+#define ENOTSOCK __WASI_ERRNO_NOTSOCK
+#define ENOTSUP __WASI_ERRNO_NOTSUP
+#define ENOTTY __WASI_ERRNO_NOTTY
+#define ENXIO __WASI_ERRNO_NXIO
+#define EOVERFLOW __WASI_ERRNO_OVERFLOW
+#define EOWNERDEAD __WASI_ERRNO_OWNERDEAD
+#define EPERM __WASI_ERRNO_PERM
+#define EPIPE __WASI_ERRNO_PIPE
+#define EPROTO __WASI_ERRNO_PROTO
+#define EPROTONOSUPPORT __WASI_ERRNO_PROTONOSUPPORT
+#define EPROTOTYPE __WASI_ERRNO_PROTOTYPE
+#define ERANGE __WASI_ERRNO_RANGE
+#define EROFS __WASI_ERRNO_ROFS
+#define ESPIPE __WASI_ERRNO_SPIPE
+#define ESRCH __WASI_ERRNO_SRCH
+#define ESTALE __WASI_ERRNO_STALE
+#define ETIMEDOUT __WASI_ERRNO_TIMEDOUT
+#define ETXTBSY __WASI_ERRNO_TXTBSY
+#define EXDEV __WASI_ERRNO_XDEV
+#define ENOTCAPABLE __WASI_ERRNO_NOTCAPABLE
+
+#define EOPNOTSUPP ENOTSUP
+#define EWOULDBLOCK EAGAIN
+
+#endif
diff --git a/libc-bottom-half/headers/public/__fd_set.h b/libc-bottom-half/headers/public/__fd_set.h
new file mode 100644
index 0000000..7be0b9f
--- /dev/null
+++ b/libc-bottom-half/headers/public/__fd_set.h
@@ -0,0 +1,76 @@
+#ifndef __wasilibc___fd_set_h
+#define __wasilibc___fd_set_h
+
+#include <__typedef_fd_set.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static __inline void FD_CLR(int __fd, fd_set *__set) {
+ size_t __n = __set->__nfds;
+ for (int *__p = __set->__fds, *__e = __p + __n;
+ __p < __e; ++__p)
+ {
+ if (*__p == __fd) {
+ *__p = __e[-1];
+ __set->__nfds = __n - 1;
+ return;
+ }
+ }
+}
+
+static __inline
+#ifdef __cplusplus
+bool
+#else
+_Bool
+#endif
+FD_ISSET(int __fd, const fd_set *__set)
+{
+ size_t __n = __set->__nfds;
+ for (const int *__p = __set->__fds, *__e = __p + __n;
+ __p < __e; ++__p)
+ {
+ if (*__p == __fd) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static __inline void FD_SET(int __fd, fd_set *__set) {
+ size_t __n = __set->__nfds;
+ for (const int *__p = __set->__fds, *__e = __p + __n;
+ __p < __e; ++__p)
+ {
+ if (*__p == __fd) {
+ return;
+ }
+ }
+ __set->__nfds = __n + 1;
+ __set->__fds[__n] = __fd;
+}
+
+static __inline void FD_ZERO(fd_set *__set) {
+ __set->__nfds = 0;
+}
+
+static __inline void FD_COPY(const fd_set *__restrict __from,
+ fd_set *__restrict __to) {
+ size_t __n = __from->__nfds;
+ __to->__nfds = __n;
+ __builtin_memcpy(__to->__fds, __from->__fds, __n * sizeof(int));
+}
+
+#define FD_CLR(fd, set) (FD_CLR((fd), (set)))
+#define FD_ISSET(fd, set) (FD_ISSET((fd), (set)))
+#define FD_SET(fd, set) (FD_SET((fd), (set)))
+#define FD_ZERO(set) (FD_ZERO((set)))
+#define FD_COPY(from, to) (FD_COPY((from), (to)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__function___isatty.h b/libc-bottom-half/headers/public/__function___isatty.h
new file mode 100644
index 0000000..7c04645
--- /dev/null
+++ b/libc-bottom-half/headers/public/__function___isatty.h
@@ -0,0 +1,14 @@
+#ifndef __wasilibc___function___isatty_h
+#define __wasilibc___function___isatty_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int __isatty(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__functions_malloc.h b/libc-bottom-half/headers/public/__functions_malloc.h
new file mode 100644
index 0000000..d516bc3
--- /dev/null
+++ b/libc-bottom-half/headers/public/__functions_malloc.h
@@ -0,0 +1,26 @@
+#ifndef __wasilibc___functions_malloc_h
+#define __wasilibc___functions_malloc_h
+
+#define __need_size_t
+#define __need_wchar_t
+#define __need_NULL
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *malloc(size_t __size) __attribute__((__malloc__, __warn_unused_result__));
+void free(void *__ptr);
+void *calloc(size_t __nmemb, size_t __size) __attribute__((__malloc__, __warn_unused_result__));
+void *realloc(void *__ptr, size_t __size) __attribute__((__warn_unused_result__));
+
+#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
+void *reallocarray(void *__ptr, size_t __nmemb, size_t __size) __attribute__((__warn_unused_result__));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__functions_memcpy.h b/libc-bottom-half/headers/public/__functions_memcpy.h
new file mode 100644
index 0000000..253b065
--- /dev/null
+++ b/libc-bottom-half/headers/public/__functions_memcpy.h
@@ -0,0 +1,20 @@
+#ifndef __wasilibc___functions_memcpy_h
+#define __wasilibc___functions_memcpy_h
+
+#define __need_size_t
+#define __need_NULL
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *memcpy(void *__restrict__ __dst, const void *__restrict__ __src, size_t __n) __attribute__((__nothrow__, __leaf__, __nonnull__(1, 2)));
+void *memmove(void *__dst, const void *__src, size_t __n) __attribute__((__nothrow__, __leaf__, __nonnull__(1, 2)));
+void *memset(void *__dst, int __c, size_t __n) __attribute__((__nothrow__, __leaf__, __nonnull__(1)));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_dirent.h b/libc-bottom-half/headers/public/__header_dirent.h
new file mode 100644
index 0000000..ccf3def
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_dirent.h
@@ -0,0 +1,45 @@
+#ifndef __wasilibc___header_dirent_h
+#define __wasilibc___header_dirent_h
+
+#include <wasi/api.h>
+
+#define DT_BLK __WASI_FILETYPE_BLOCK_DEVICE
+#define DT_CHR __WASI_FILETYPE_CHARACTER_DEVICE
+#define DT_DIR __WASI_FILETYPE_DIRECTORY
+#define DT_FIFO __WASI_FILETYPE_SOCKET_STREAM
+#define DT_LNK __WASI_FILETYPE_SYMBOLIC_LINK
+#define DT_REG __WASI_FILETYPE_REGULAR_FILE
+#define DT_UNKNOWN __WASI_FILETYPE_UNKNOWN
+
+#define IFTODT(x) (__wasilibc_iftodt(x))
+#define DTTOIF(x) (__wasilibc_dttoif(x))
+
+int __wasilibc_iftodt(int x);
+int __wasilibc_dttoif(int x);
+
+#include <__struct_dirent.h>
+#include <__typedef_DIR.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int closedir(DIR *);
+DIR *opendir(const char *);
+DIR *fdopendir(int);
+int fdclosedir(DIR *);
+struct dirent *readdir(DIR *);
+void rewinddir(DIR *);
+void seekdir(DIR *, long);
+long telldir(DIR *);
+DIR *opendirat(int, const char *);
+void rewinddir(DIR *);
+int scandirat(int, const char *, struct dirent ***,
+ int (*)(const struct dirent *),
+ int (*)(const struct dirent **, const struct dirent **));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_fcntl.h b/libc-bottom-half/headers/public/__header_fcntl.h
new file mode 100644
index 0000000..04252ac
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_fcntl.h
@@ -0,0 +1,61 @@
+#ifndef __wasilibc___header_fcntl_h
+#define __wasilibc___header_fcntl_h
+
+#include <wasi/api.h>
+#include <__seek.h>
+#include <__mode_t.h>
+
+#define O_APPEND __WASI_FDFLAGS_APPEND
+#define O_DSYNC __WASI_FDFLAGS_DSYNC
+#define O_NONBLOCK __WASI_FDFLAGS_NONBLOCK
+#define O_RSYNC __WASI_FDFLAGS_RSYNC
+#define O_SYNC __WASI_FDFLAGS_SYNC
+#define O_CREAT (__WASI_OFLAGS_CREAT << 12)
+#define O_DIRECTORY (__WASI_OFLAGS_DIRECTORY << 12)
+#define O_EXCL (__WASI_OFLAGS_EXCL << 12)
+#define O_TRUNC (__WASI_OFLAGS_TRUNC << 12)
+
+#define O_NOFOLLOW (0x01000000)
+#define O_EXEC (0x02000000)
+#define O_RDONLY (0x04000000)
+#define O_SEARCH (0x08000000)
+#define O_WRONLY (0x10000000)
+
+/*
+ * O_CLOEXEC is defined to be zero, as WASI has no exec-style functions.
+ */
+#define O_CLOEXEC (0)
+
+/*
+ * O_TTY_INIT is defined to be zero, meaning that WASI implementations are
+ * expected to always initialize a terminal the first time it's opened.
+ */
+#define O_TTY_INIT (0)
+
+#define O_NOCTTY (0)
+
+#define O_RDWR (O_RDONLY | O_WRONLY)
+#define O_ACCMODE (O_EXEC | O_RDWR | O_SEARCH)
+
+#define POSIX_FADV_DONTNEED __WASI_ADVICE_DONTNEED
+#define POSIX_FADV_NOREUSE __WASI_ADVICE_NOREUSE
+#define POSIX_FADV_NORMAL __WASI_ADVICE_NORMAL
+#define POSIX_FADV_RANDOM __WASI_ADVICE_RANDOM
+#define POSIX_FADV_SEQUENTIAL __WASI_ADVICE_SEQUENTIAL
+#define POSIX_FADV_WILLNEED __WASI_ADVICE_WILLNEED
+
+#define F_GETFD (1)
+#define F_SETFD (2)
+#define F_GETFL (3)
+#define F_SETFL (4)
+
+#define FD_CLOEXEC (1)
+
+#define AT_EACCESS (0x0)
+#define AT_SYMLINK_NOFOLLOW (0x1)
+#define AT_SYMLINK_FOLLOW (0x2)
+#define AT_REMOVEDIR (0x4)
+
+#define AT_FDCWD (-2)
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_inttypes.h b/libc-bottom-half/headers/public/__header_inttypes.h
new file mode 100644
index 0000000..47c5c9b
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_inttypes.h
@@ -0,0 +1,182 @@
+#ifndef __wasilibc___include_inttypes_h
+#define __wasilibc___include_inttypes_h
+
+#include <stdint.h>
+
+#define __need_wchar_t
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct { intmax_t quot, rem; } imaxdiv_t;
+
+intmax_t imaxabs(intmax_t);
+imaxdiv_t imaxdiv(intmax_t, intmax_t);
+intmax_t strtoimax(const char *__restrict, char **__restrict, int);
+uintmax_t strtoumax(const char *__restrict, char **__restrict, int);
+intmax_t wcstoimax(const wchar_t *__restrict, wchar_t **__restrict, int);
+uintmax_t wcstoumax(const wchar_t *__restrict, wchar_t **__restrict, int);
+
+#define PRId16 __INT16_FMTd__
+#define PRIi16 __INT16_FMTi__
+#define PRId32 __INT32_FMTd__
+#define PRIi32 __INT32_FMTi__
+#define PRId64 __INT64_FMTd__
+#define PRIi64 __INT64_FMTi__
+#define PRId8 __INT8_FMTd__
+#define PRIi8 __INT8_FMTi__
+#define PRIdMAX __INTMAX_FMTd__
+#define PRIiMAX __INTMAX_FMTi__
+#define PRIdPTR __INTPTR_FMTd__
+#define PRIiPTR __INTPTR_FMTi__
+#define PRIdFAST16 __INT_FAST16_FMTd__
+#define PRIiFAST16 __INT_FAST16_FMTi__
+#define PRIdFAST32 __INT_FAST32_FMTd__
+#define PRIiFAST32 __INT_FAST32_FMTi__
+#define PRIdFAST64 __INT_FAST64_FMTd__
+#define PRIiFAST64 __INT_FAST64_FMTi__
+#define PRIdFAST8 __INT_FAST8_FMTd__
+#define PRIiFAST8 __INT_FAST8_FMTi__
+#define PRIdLEAST16 __INT_LEAST16_FMTd__
+#define PRIiLEAST16 __INT_LEAST16_FMTi__
+#define PRIdLEAST32 __INT_LEAST32_FMTd__
+#define PRIiLEAST32 __INT_LEAST32_FMTi__
+#define PRIdLEAST64 __INT_LEAST64_FMTd__
+#define PRIiLEAST64 __INT_LEAST64_FMTi__
+#define PRIdLEAST8 __INT_LEAST8_FMTd__
+#define PRIiLEAST8 __INT_LEAST8_FMTi__
+#define PRIX16 __UINT16_FMTX__
+#define PRIo16 __UINT16_FMTo__
+#define PRIu16 __UINT16_FMTu__
+#define PRIx16 __UINT16_FMTx__
+#define PRIX32 __UINT32_FMTX__
+#define PRIo32 __UINT32_FMTo__
+#define PRIu32 __UINT32_FMTu__
+#define PRIx32 __UINT32_FMTx__
+#define PRIX64 __UINT64_FMTX__
+#define PRIo64 __UINT64_FMTo__
+#define PRIu64 __UINT64_FMTu__
+#define PRIx64 __UINT64_FMTx__
+#define PRIX8 __UINT8_FMTX__
+#define PRIo8 __UINT8_FMTo__
+#define PRIu8 __UINT8_FMTu__
+#define PRIx8 __UINT8_FMTx__
+#define PRIXMAX __UINTMAX_FMTX__
+#define PRIoMAX __UINTMAX_FMTo__
+#define PRIuMAX __UINTMAX_FMTu__
+#define PRIxMAX __UINTMAX_FMTx__
+#define PRIXPTR __UINTPTR_FMTX__
+#define PRIoPTR __UINTPTR_FMTo__
+#define PRIuPTR __UINTPTR_FMTu__
+#define PRIxPTR __UINTPTR_FMTx__
+#define PRIXFAST16 __UINT_FAST16_FMTX__
+#define PRIoFAST16 __UINT_FAST16_FMTo__
+#define PRIuFAST16 __UINT_FAST16_FMTu__
+#define PRIxFAST16 __UINT_FAST16_FMTx__
+#define PRIXFAST32 __UINT_FAST32_FMTX__
+#define PRIoFAST32 __UINT_FAST32_FMTo__
+#define PRIuFAST32 __UINT_FAST32_FMTu__
+#define PRIxFAST32 __UINT_FAST32_FMTx__
+#define PRIXFAST64 __UINT_FAST64_FMTX__
+#define PRIoFAST64 __UINT_FAST64_FMTo__
+#define PRIuFAST64 __UINT_FAST64_FMTu__
+#define PRIxFAST64 __UINT_FAST64_FMTx__
+#define PRIXFAST8 __UINT_FAST8_FMTX__
+#define PRIoFAST8 __UINT_FAST8_FMTo__
+#define PRIuFAST8 __UINT_FAST8_FMTu__
+#define PRIxFAST8 __UINT_FAST8_FMTx__
+#define PRIXLEAST16 __UINT_LEAST16_FMTX__
+#define PRIoLEAST16 __UINT_LEAST16_FMTo__
+#define PRIuLEAST16 __UINT_LEAST16_FMTu__
+#define PRIxLEAST16 __UINT_LEAST16_FMTx__
+#define PRIXLEAST32 __UINT_LEAST32_FMTX__
+#define PRIoLEAST32 __UINT_LEAST32_FMTo__
+#define PRIuLEAST32 __UINT_LEAST32_FMTu__
+#define PRIxLEAST32 __UINT_LEAST32_FMTx__
+#define PRIXLEAST64 __UINT_LEAST64_FMTX__
+#define PRIoLEAST64 __UINT_LEAST64_FMTo__
+#define PRIuLEAST64 __UINT_LEAST64_FMTu__
+#define PRIxLEAST64 __UINT_LEAST64_FMTx__
+#define PRIXLEAST8 __UINT_LEAST8_FMTX__
+#define PRIoLEAST8 __UINT_LEAST8_FMTo__
+#define PRIuLEAST8 __UINT_LEAST8_FMTu__
+#define PRIxLEAST8 __UINT_LEAST8_FMTx__
+
+#define SCNd16 __INT16_FMTd__
+#define SCNi16 __INT16_FMTi__
+#define SCNd32 __INT32_FMTd__
+#define SCNi32 __INT32_FMTi__
+#define SCNd64 __INT64_FMTd__
+#define SCNi64 __INT64_FMTi__
+#define SCNd8 __INT8_FMTd__
+#define SCNi8 __INT8_FMTi__
+#define SCNdMAX __INTMAX_FMTd__
+#define SCNiMAX __INTMAX_FMTi__
+#define SCNdPTR __INTPTR_FMTd__
+#define SCNiPTR __INTPTR_FMTi__
+#define SCNdFAST16 __INT_FAST16_FMTd__
+#define SCNiFAST16 __INT_FAST16_FMTi__
+#define SCNdFAST32 __INT_FAST32_FMTd__
+#define SCNiFAST32 __INT_FAST32_FMTi__
+#define SCNdFAST64 __INT_FAST64_FMTd__
+#define SCNiFAST64 __INT_FAST64_FMTi__
+#define SCNdFAST8 __INT_FAST8_FMTd__
+#define SCNiFAST8 __INT_FAST8_FMTi__
+#define SCNdLEAST16 __INT_LEAST16_FMTd__
+#define SCNiLEAST16 __INT_LEAST16_FMTi__
+#define SCNdLEAST32 __INT_LEAST32_FMTd__
+#define SCNiLEAST32 __INT_LEAST32_FMTi__
+#define SCNdLEAST64 __INT_LEAST64_FMTd__
+#define SCNiLEAST64 __INT_LEAST64_FMTi__
+#define SCNdLEAST8 __INT_LEAST8_FMTd__
+#define SCNiLEAST8 __INT_LEAST8_FMTi__
+#define SCNo16 __UINT16_FMTo__
+#define SCNu16 __UINT16_FMTu__
+#define SCNx16 __UINT16_FMTx__
+#define SCNo32 __UINT32_FMTo__
+#define SCNu32 __UINT32_FMTu__
+#define SCNx32 __UINT32_FMTx__
+#define SCNo64 __UINT64_FMTo__
+#define SCNu64 __UINT64_FMTu__
+#define SCNx64 __UINT64_FMTx__
+#define SCNo8 __UINT8_FMTo__
+#define SCNu8 __UINT8_FMTu__
+#define SCNx8 __UINT8_FMTx__
+#define SCNoMAX __UINTMAX_FMTo__
+#define SCNuMAX __UINTMAX_FMTu__
+#define SCNxMAX __UINTMAX_FMTx__
+#define SCNoPTR __UINTPTR_FMTo__
+#define SCNuPTR __UINTPTR_FMTu__
+#define SCNxPTR __UINTPTR_FMTx__
+#define SCNoFAST16 __UINT_FAST16_FMTo__
+#define SCNuFAST16 __UINT_FAST16_FMTu__
+#define SCNxFAST16 __UINT_FAST16_FMTx__
+#define SCNoFAST32 __UINT_FAST32_FMTo__
+#define SCNuFAST32 __UINT_FAST32_FMTu__
+#define SCNxFAST32 __UINT_FAST32_FMTx__
+#define SCNoFAST64 __UINT_FAST64_FMTo__
+#define SCNuFAST64 __UINT_FAST64_FMTu__
+#define SCNxFAST64 __UINT_FAST64_FMTx__
+#define SCNoFAST8 __UINT_FAST8_FMTo__
+#define SCNuFAST8 __UINT_FAST8_FMTu__
+#define SCNxFAST8 __UINT_FAST8_FMTx__
+#define SCNoLEAST16 __UINT_LEAST16_FMTo__
+#define SCNuLEAST16 __UINT_LEAST16_FMTu__
+#define SCNxLEAST16 __UINT_LEAST16_FMTx__
+#define SCNoLEAST32 __UINT_LEAST32_FMTo__
+#define SCNuLEAST32 __UINT_LEAST32_FMTu__
+#define SCNxLEAST32 __UINT_LEAST32_FMTx__
+#define SCNoLEAST64 __UINT_LEAST64_FMTo__
+#define SCNuLEAST64 __UINT_LEAST64_FMTu__
+#define SCNxLEAST64 __UINT_LEAST64_FMTx__
+#define SCNoLEAST8 __UINT_LEAST8_FMTo__
+#define SCNuLEAST8 __UINT_LEAST8_FMTu__
+#define SCNxLEAST8 __UINT_LEAST8_FMTx__
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_netinet_in.h b/libc-bottom-half/headers/public/__header_netinet_in.h
new file mode 100644
index 0000000..30359c8
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_netinet_in.h
@@ -0,0 +1,30 @@
+#ifndef __wasilibc___header_netinet_in_h
+#define __wasilibc___header_netinet_in_h
+
+#include <__struct_in_addr.h>
+#include <__struct_in6_addr.h>
+#include <__struct_sockaddr_in.h>
+#include <__struct_sockaddr_in6.h>
+
+#define IPPROTO_IP 0
+#define IPPROTO_ICMP 1
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+#define IPPROTO_IPV6 41
+#define IPPROTO_RAW 255
+
+#define IN6ADDR_ANY_INIT { { \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00 \
+} }
+
+#define IN6ADDR_LOOPBACK_INIT { { \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x01 \
+} }
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_poll.h b/libc-bottom-half/headers/public/__header_poll.h
new file mode 100644
index 0000000..23b36f2
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_poll.h
@@ -0,0 +1,27 @@
+#ifndef __wasilibc___header_poll_h
+#define __wasilibc___header_poll_h
+
+#include <__struct_pollfd.h>
+#include <__typedef_nfds_t.h>
+
+#define POLLRDNORM 0x1
+#define POLLWRNORM 0x2
+
+#define POLLIN POLLRDNORM
+#define POLLOUT POLLWRNORM
+
+#define POLLERR 0x1000
+#define POLLHUP 0x2000
+#define POLLNVAL 0x4000
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int poll(struct pollfd[], nfds_t, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_stdlib.h b/libc-bottom-half/headers/public/__header_stdlib.h
new file mode 100644
index 0000000..a6c49a5
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_stdlib.h
@@ -0,0 +1,21 @@
+#ifndef __wasilibc___header_stdlib_h
+#define __wasilibc___header_stdlib_h
+
+#define __need_size_t
+#include <stddef.h>
+
+#include <__functions_malloc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void abort(void) __attribute__((__noreturn__));
+void qsort(void *, size_t, size_t, int (*)(const void *, const void *));
+void _Exit(int) __attribute__((__noreturn__));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_string.h b/libc-bottom-half/headers/public/__header_string.h
new file mode 100644
index 0000000..eb98a8a
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_string.h
@@ -0,0 +1,23 @@
+#ifndef __wasilibc___header_string_h
+#define __wasilibc___header_string_h
+
+#define __need_size_t
+#define __need_NULL
+#include <stddef.h>
+
+#include <__functions_memcpy.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+size_t strlen(const char *) __attribute__((__nothrow__, __leaf__, __pure__, __nonnull__(1)));
+char *strdup(const char *) __attribute__((__nothrow__, __nonnull__(1)));
+int strcmp(const char *, const char *) __attribute__((__nothrow__, __pure__, __nonnull__(1, 2)));
+void *memchr(const void *, int, size_t) __attribute__((__nothrow__, __pure__, __nonnull__(1)));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_sys_ioctl.h b/libc-bottom-half/headers/public/__header_sys_ioctl.h
new file mode 100644
index 0000000..f2cf4ee
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_sys_ioctl.h
@@ -0,0 +1,17 @@
+#ifndef __wasilibc___header_sys_ioctl_h
+#define __wasilibc___header_sys_ioctl_h
+
+#define FIONREAD 1
+#define FIONBIO 2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ioctl(int, int, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_sys_resource.h b/libc-bottom-half/headers/public/__header_sys_resource.h
new file mode 100644
index 0000000..bae9d5a
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_sys_resource.h
@@ -0,0 +1,19 @@
+#ifndef __wasilibc___header_sys_resource_h
+#define __wasilibc___header_sys_resource_h
+
+#include <__struct_rusage.h>
+
+#define RUSAGE_SELF 1
+#define RUSAGE_CHILDREN 2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int getrusage(int who, struct rusage *usage);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_sys_socket.h b/libc-bottom-half/headers/public/__header_sys_socket.h
new file mode 100644
index 0000000..9fa8684
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_sys_socket.h
@@ -0,0 +1,41 @@
+#ifndef __wasilibc___header_sys_socket_h
+#define __wasilibc___header_sys_socket_h
+
+#include <__struct_msghdr.h>
+#include <__struct_sockaddr.h>
+#include <__struct_sockaddr_storage.h>
+
+#include <wasi/api.h>
+
+#define SHUT_RD __WASI_SDFLAGS_RD
+#define SHUT_WR __WASI_SDFLAGS_WR
+#define SHUT_RDWR (SHUT_RD | SHUT_WR)
+
+#define MSG_PEEK __WASI_RIFLAGS_RECV_PEEK
+#define MSG_WAITALL __WASI_RIFLAGS_RECV_WAITALL
+#define MSG_TRUNC __WASI_RIFLAGS_RECV_DATA_TRUNCATED
+
+#define SOCK_DGRAM __WASI_FILETYPE_SOCKET_DGRAM
+#define SOCK_STREAM __WASI_FILETYPE_SOCKET_STREAM
+
+#define SOCK_NONBLOCK (0x00004000)
+#define SOCK_CLOEXEC (0x00002000)
+
+#define SOL_SOCKET 0x7fffffff
+
+#define SO_TYPE 3
+
+#define AF_UNSPEC 0
+#define AF_INET 1
+#define AF_INET6 2
+#define AF_UNIX 3
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_sys_stat.h b/libc-bottom-half/headers/public/__header_sys_stat.h
new file mode 100644
index 0000000..f965cf3
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_sys_stat.h
@@ -0,0 +1,15 @@
+#ifndef __wasilibc___header_sys_stat_h
+#define __wasilibc___header_sys_stat_h
+
+#include <__struct_stat.h>
+
+#define st_atime st_atim.tv_sec
+#define st_mtime st_mtim.tv_sec
+#define st_ctime st_ctim.tv_sec
+
+#include <__mode_t.h>
+
+#define UTIME_NOW (-1)
+#define UTIME_OMIT (-2)
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_time.h b/libc-bottom-half/headers/public/__header_time.h
new file mode 100644
index 0000000..2a2ff91
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_time.h
@@ -0,0 +1,33 @@
+#ifndef __wasilibc___header_time_h
+#define __wasilibc___header_time_h
+
+#define __need_size_t
+#define __need_NULL
+#include <stddef.h>
+
+#include <__typedef_time_t.h>
+#include <__struct_timespec.h>
+#include <__struct_tm.h>
+#include <__typedef_clockid_t.h>
+
+#include <wasi/api.h>
+
+#define TIMER_ABSTIME __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME
+
+extern const struct __clockid _CLOCK_MONOTONIC;
+#define CLOCK_MONOTONIC (&_CLOCK_MONOTONIC)
+extern const struct __clockid _CLOCK_REALTIME;
+#define CLOCK_REALTIME (&_CLOCK_REALTIME)
+
+/*
+ * TIME_UTC is the only standardized time base value.
+ */
+#define TIME_UTC 1
+
+/*
+ * Note that XSI specifies CLOCKS_PER_SEC to be 1000000, rather than
+ * 1000000000; the clock API is providing more precision than XSI specifies.
+ */
+#define CLOCKS_PER_SEC ((clock_t)1000000000)
+
+#endif
diff --git a/libc-bottom-half/headers/public/__header_unistd.h b/libc-bottom-half/headers/public/__header_unistd.h
new file mode 100644
index 0000000..6b80d63
--- /dev/null
+++ b/libc-bottom-half/headers/public/__header_unistd.h
@@ -0,0 +1,28 @@
+#ifndef __wasilibc___header_unistd_h
+#define __wasilibc___header_unistd_h
+
+struct stat;
+
+#include <__seek.h>
+
+#define F_OK (0)
+#define X_OK (1)
+#define W_OK (2)
+#define R_OK (4)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int close(int fd);
+int faccessat(int, const char *, int, int);
+int fstatat(int, const char *__restrict, struct stat *__restrict, int);
+int renameat(int, const char *, int, const char *);
+int openat(int, const char *, int, ...);
+void *sbrk(intptr_t increment);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/__macro_FD_SETSIZE.h b/libc-bottom-half/headers/public/__macro_FD_SETSIZE.h
new file mode 100644
index 0000000..bb88e45
--- /dev/null
+++ b/libc-bottom-half/headers/public/__macro_FD_SETSIZE.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___macro_FD_SETSIZE_h
+#define __wasilibc___macro_FD_SETSIZE_h
+
+#define FD_SETSIZE 1024
+
+#endif
diff --git a/libc-bottom-half/headers/public/__macro_PAGESIZE.h b/libc-bottom-half/headers/public/__macro_PAGESIZE.h
new file mode 100644
index 0000000..d892220
--- /dev/null
+++ b/libc-bottom-half/headers/public/__macro_PAGESIZE.h
@@ -0,0 +1,16 @@
+#ifndef __wasilibc___macro_PAGESIZE_h
+#define __wasilibc___macro_PAGESIZE_h
+
+/*
+ * The page size in WebAssembly is fixed at 64 KiB. If this ever changes,
+ * it's expected that applications will need to opt in, so we can change
+ * this.
+ *
+ * If this ever needs to be a value outside the range of an `int`, the
+ * `getpagesize` function which returns this value will need special
+ * consideration. POSIX has deprecated `getpagesize` in favor of
+ * `sysconf(_SC_PAGESIZE)` which does not have this problem.
+ */
+#define PAGESIZE (0x10000)
+
+#endif
diff --git a/libc-bottom-half/headers/public/__mode_t.h b/libc-bottom-half/headers/public/__mode_t.h
new file mode 100644
index 0000000..c0beef5
--- /dev/null
+++ b/libc-bottom-half/headers/public/__mode_t.h
@@ -0,0 +1,38 @@
+#ifndef __wasilibc___mode_t_h
+#define __wasilibc___mode_t_h
+
+#define S_IFMT \
+ (S_IFBLK | S_IFCHR | S_IFDIR | S_IFIFO | S_IFLNK | S_IFREG | S_IFSOCK)
+#define S_IFBLK (0x6000)
+#define S_IFCHR (0x2000)
+#define S_IFDIR (0x4000)
+#define S_IFLNK (0xa000)
+#define S_IFREG (0x8000)
+#define S_IFSOCK (0xc000)
+#define S_IFIFO (0xc000)
+
+#define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK)
+#define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR)
+#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
+#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO)
+#define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK)
+#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
+#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK)
+
+#define S_IXOTH (0x1)
+#define S_IWOTH (0x2)
+#define S_IROTH (0x4)
+#define S_IRWXO (S_IXOTH | S_IWOTH | S_IROTH)
+#define S_IXGRP (0x8)
+#define S_IWGRP (0x10)
+#define S_IRGRP (0x20)
+#define S_IRWXG (S_IXGRP | S_IWGRP | S_IRGRP)
+#define S_IXUSR (0x40)
+#define S_IWUSR (0x80)
+#define S_IRUSR (0x100)
+#define S_IRWXU (S_IXUSR | S_IWUSR | S_IRUSR)
+#define S_ISVTX (0x200)
+#define S_ISGID (0x400)
+#define S_ISUID (0x800)
+
+#endif
diff --git a/libc-bottom-half/headers/public/__seek.h b/libc-bottom-half/headers/public/__seek.h
new file mode 100644
index 0000000..8824adb
--- /dev/null
+++ b/libc-bottom-half/headers/public/__seek.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc___seek_h
+#define __wasilibc___seek_h
+
+#include <wasi/api.h>
+
+#define SEEK_CUR __WASI_WHENCE_CUR
+#define SEEK_END __WASI_WHENCE_END
+#define SEEK_SET __WASI_WHENCE_SET
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_dirent.h b/libc-bottom-half/headers/public/__struct_dirent.h
new file mode 100644
index 0000000..9d2ece8
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_dirent.h
@@ -0,0 +1,14 @@
+#ifndef __wasilibc___struct_dirent_h
+#define __wasilibc___struct_dirent_h
+
+#include <__typedef_ino_t.h>
+
+#define _DIRENT_HAVE_D_TYPE
+
+struct dirent {
+ ino_t d_ino;
+ unsigned char d_type;
+ char d_name[];
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_in6_addr.h b/libc-bottom-half/headers/public/__struct_in6_addr.h
new file mode 100644
index 0000000..cf2fafe
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_in6_addr.h
@@ -0,0 +1,8 @@
+#ifndef __wasilibc___struct_in6_addr_h
+#define __wasilibc___struct_in6_addr_h
+
+struct in6_addr {
+ _Alignas(int32_t) unsigned char s6_addr[16];
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_in_addr.h b/libc-bottom-half/headers/public/__struct_in_addr.h
new file mode 100644
index 0000000..881ffa6
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_in_addr.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc___struct_in_addr_h
+#define __wasilibc___struct_in_addr_h
+
+#include <__typedef_in_addr_t.h>
+
+struct in_addr {
+ in_addr_t s_addr;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_iovec.h b/libc-bottom-half/headers/public/__struct_iovec.h
new file mode 100644
index 0000000..da3e1c7
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_iovec.h
@@ -0,0 +1,12 @@
+#ifndef __wasilibc___struct_iovec_h
+#define __wasilibc___struct_iovec_h
+
+#define __need_size_t
+#include <stddef.h>
+
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_msghdr.h b/libc-bottom-half/headers/public/__struct_msghdr.h
new file mode 100644
index 0000000..0c0877d
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_msghdr.h
@@ -0,0 +1,16 @@
+#ifndef __wasilibc___struct_msghdr_h
+#define __wasilibc___struct_msghdr_h
+
+#include <__typedef_socklen_t.h>
+
+struct msghdr {
+ void *msg_name;
+ socklen_t msg_namelen;
+ struct iovec *msg_iov;
+ int msg_iovlen;
+ void *msg_control;
+ socklen_t msg_controllen;
+ int msg_flags;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_pollfd.h b/libc-bottom-half/headers/public/__struct_pollfd.h
new file mode 100644
index 0000000..45874c5
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_pollfd.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc___struct_pollfd_h
+#define __wasilibc___struct_pollfd_h
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_rusage.h b/libc-bottom-half/headers/public/__struct_rusage.h
new file mode 100644
index 0000000..69fe2fe
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_rusage.h
@@ -0,0 +1,12 @@
+#ifndef __wasilibc___struct_rusage_h
+#define __wasilibc___struct_rusage_h
+
+#include <__struct_timeval.h>
+
+/* TODO: Add more features here. */
+struct rusage {
+ struct timeval ru_utime;
+ struct timeval ru_stime;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_sockaddr.h b/libc-bottom-half/headers/public/__struct_sockaddr.h
new file mode 100644
index 0000000..9891b90
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_sockaddr.h
@@ -0,0 +1,14 @@
+#ifndef __wasilibc___struct_sockaddr_h
+#define __wasilibc___struct_sockaddr_h
+
+#define __need_STDDEF_H_misc
+#include <stddef.h>
+
+#include <__typedef_sa_family_t.h>
+
+struct sockaddr {
+ __attribute__((aligned(__BIGGEST_ALIGNMENT__))) sa_family_t sa_family;
+ char sa_data[0];
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_in.h b/libc-bottom-half/headers/public/__struct_sockaddr_in.h
new file mode 100644
index 0000000..73dc5c6
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_sockaddr_in.h
@@ -0,0 +1,17 @@
+#ifndef __wasilibc___struct_sockaddr_in_h
+#define __wasilibc___struct_sockaddr_in_h
+
+#define __need_STDDEF_H_misc
+#include <stddef.h>
+
+#include <__typedef_sa_family_t.h>
+#include <__typedef_in_port_t.h>
+#include <__struct_in_addr.h>
+
+struct sockaddr_in {
+ _Alignas(max_align_t) sa_family_t sin_family;
+ in_port_t sin_port;
+ struct in_addr sin_addr;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_in6.h b/libc-bottom-half/headers/public/__struct_sockaddr_in6.h
new file mode 100644
index 0000000..a220f91
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_sockaddr_in6.h
@@ -0,0 +1,19 @@
+#ifndef __wasilibc___struct_sockaddr_in6_h
+#define __wasilibc___struct_sockaddr_in6_h
+
+#define __need_STDDEF_H_misc
+#include <stddef.h>
+
+#include <__typedef_sa_family_t.h>
+#include <__typedef_in_port_t.h>
+#include <__struct_in6_addr.h>
+
+struct sockaddr_in6 {
+ _Alignas(max_align_t) sa_family_t sin6_family;
+ in_port_t sin6_port;
+ unsigned sin6_flowinfo;
+ struct in6_addr sin6_addr;
+ unsigned sin6_scope_id;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_storage.h b/libc-bottom-half/headers/public/__struct_sockaddr_storage.h
new file mode 100644
index 0000000..1ae26a7
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_sockaddr_storage.h
@@ -0,0 +1,14 @@
+#ifndef __wasilibc___struct_sockaddr_storage_h
+#define __wasilibc___struct_sockaddr_storage_h
+
+#define __need_STDDEF_H_misc
+#include <stddef.h>
+
+#include <__typedef_sa_family_t.h>
+
+struct sockaddr_storage {
+ _Alignas(max_align_t) sa_family_t ss_family;
+ char __ss_data[32];
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_un.h b/libc-bottom-half/headers/public/__struct_sockaddr_un.h
new file mode 100644
index 0000000..a5be6e5
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_sockaddr_un.h
@@ -0,0 +1,13 @@
+#ifndef __wasilibc___struct_sockaddr_un_h
+#define __wasilibc___struct_sockaddr_un_h
+
+#define __need_STDDEF_H_misc
+#include <stddef.h>
+
+#include <__typedef_sa_family_t.h>
+
+struct sockaddr_un {
+ _Alignas(max_align_t) sa_family_t sun_family;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_stat.h b/libc-bottom-half/headers/public/__struct_stat.h
new file mode 100644
index 0000000..5e345da
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_stat.h
@@ -0,0 +1,35 @@
+#ifndef __wasilibc___struct_stat_h
+#define __wasilibc___struct_stat_h
+
+#include <__typedef_dev_t.h>
+#include <__typedef_ino_t.h>
+#include <__typedef_nlink_t.h>
+#include <__typedef_mode_t.h>
+#include <__typedef_uid_t.h>
+#include <__typedef_gid_t.h>
+#include <__typedef_off_t.h>
+#include <__typedef_blksize_t.h>
+#include <__typedef_blkcnt_t.h>
+#include <__struct_timespec.h>
+
+struct stat {
+ dev_t st_dev;
+ ino_t st_ino;
+ nlink_t st_nlink;
+
+ mode_t st_mode;
+ uid_t st_uid;
+ gid_t st_gid;
+ unsigned int __pad0;
+ dev_t st_rdev;
+ off_t st_size;
+ blksize_t st_blksize;
+ blkcnt_t st_blocks;
+
+ struct timespec st_atim;
+ struct timespec st_mtim;
+ struct timespec st_ctim;
+ long long __reserved[3];
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_timespec.h b/libc-bottom-half/headers/public/__struct_timespec.h
new file mode 100644
index 0000000..10d83f9
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_timespec.h
@@ -0,0 +1,12 @@
+#ifndef __wasilibc___struct_timespec_h
+#define __wasilibc___struct_timespec_h
+
+#include <__typedef_time_t.h>
+
+/* As specified in POSIX. */
+struct timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_timeval.h b/libc-bottom-half/headers/public/__struct_timeval.h
new file mode 100644
index 0000000..b09f1a9
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_timeval.h
@@ -0,0 +1,13 @@
+#ifndef __wasilibc___struct_timeval_h
+#define __wasilibc___struct_timeval_h
+
+#include <__typedef_time_t.h>
+#include <__typedef_suseconds_t.h>
+
+/* As specified in POSIX. */
+struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_tm.h b/libc-bottom-half/headers/public/__struct_tm.h
new file mode 100644
index 0000000..3c83dc5
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_tm.h
@@ -0,0 +1,19 @@
+#ifndef __wasilibc___struct_tm_h
+#define __wasilibc___struct_tm_h
+
+struct tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ int __tm_gmtoff;
+ const char *__tm_zone;
+ int __tm_nsec;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__struct_tms.h b/libc-bottom-half/headers/public/__struct_tms.h
new file mode 100644
index 0000000..9914097
--- /dev/null
+++ b/libc-bottom-half/headers/public/__struct_tms.h
@@ -0,0 +1,13 @@
+#ifndef __wasilibc___struct_tms_h
+#define __wasilibc___struct_tms_h
+
+#include <__typedef_clock_t.h>
+
+struct tms {
+ clock_t tms_utime;
+ clock_t tms_stime;
+ clock_t tms_cutime;
+ clock_t tms_cstime;
+};
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_DIR.h b/libc-bottom-half/headers/public/__typedef_DIR.h
new file mode 100644
index 0000000..f07cfc1
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_DIR.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_DIR_h
+#define __wasilibc___typedef_DIR_h
+
+typedef struct _DIR DIR;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_blkcnt_t.h b/libc-bottom-half/headers/public/__typedef_blkcnt_t.h
new file mode 100644
index 0000000..e8d7b3d
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_blkcnt_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_blkcnt_t_h
+#define __wasilibc___typedef_blkcnt_t_h
+
+/* Define these as 64-bit signed integers to support files larger than 2 GiB. */
+typedef long long blkcnt_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_blksize_t.h b/libc-bottom-half/headers/public/__typedef_blksize_t.h
new file mode 100644
index 0000000..8816e0a
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_blksize_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_blksize_t_h
+#define __wasilibc___typedef_blksize_t_h
+
+typedef long blksize_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_clock_t.h b/libc-bottom-half/headers/public/__typedef_clock_t.h
new file mode 100644
index 0000000..68cb588
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_clock_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_clock_t_h
+#define __wasilibc___typedef_clock_t_h
+
+/* Define this as a 64-bit signed integer to avoid wraparounds. */
+typedef long long clock_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_clockid_t.h b/libc-bottom-half/headers/public/__typedef_clockid_t.h
new file mode 100644
index 0000000..6de4244
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_clockid_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_clockid_t_h
+#define __wasilibc___typedef_clockid_t_h
+
+typedef const struct __clockid *clockid_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_dev_t.h b/libc-bottom-half/headers/public/__typedef_dev_t.h
new file mode 100644
index 0000000..353e94f
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_dev_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_dev_t_h
+#define __wasilibc___typedef_dev_t_h
+
+/* Define these as 64-bit integers to support billions of devices. */
+typedef unsigned long long dev_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_fd_set.h b/libc-bottom-half/headers/public/__typedef_fd_set.h
new file mode 100644
index 0000000..35ea958
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_fd_set.h
@@ -0,0 +1,14 @@
+#ifndef __wasilibc___typedef_fd_set_h
+#define __wasilibc___typedef_fd_set_h
+
+#define __need_size_t
+#include <stddef.h>
+
+#include <__macro_FD_SETSIZE.h>
+
+typedef struct {
+ size_t __nfds;
+ int __fds[FD_SETSIZE];
+} fd_set;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_gid_t.h b/libc-bottom-half/headers/public/__typedef_gid_t.h
new file mode 100644
index 0000000..6eb82f2
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_gid_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_gid_t_h
+#define __wasilibc___typedef_gid_t_h
+
+typedef unsigned gid_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_in_addr_t.h b/libc-bottom-half/headers/public/__typedef_in_addr_t.h
new file mode 100644
index 0000000..68ab512
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_in_addr_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_in_addr_t_h
+#define __wasilibc___typedef_in_addr_t_h
+
+typedef unsigned in_addr_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_in_port_t.h b/libc-bottom-half/headers/public/__typedef_in_port_t.h
new file mode 100644
index 0000000..890a2c8
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_in_port_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_in_port_t_h
+#define __wasilibc___typedef_in_port_t_h
+
+typedef unsigned short in_port_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_ino_t.h b/libc-bottom-half/headers/public/__typedef_ino_t.h
new file mode 100644
index 0000000..f3e11e4
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_ino_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_ino_t_h
+#define __wasilibc___typedef_ino_t_h
+
+/* Define these as 64-bit integers to support billions of inodes. */
+typedef unsigned long long ino_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_mode_t.h b/libc-bottom-half/headers/public/__typedef_mode_t.h
new file mode 100644
index 0000000..51b927d
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_mode_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_mode_t_h
+#define __wasilibc___typedef_mode_t_h
+
+typedef unsigned mode_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_nfds_t.h b/libc-bottom-half/headers/public/__typedef_nfds_t.h
new file mode 100644
index 0000000..ea0a93e
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_nfds_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_nfds_t_h
+#define __wasilibc___typedef_nfds_t_h
+
+typedef unsigned long nfds_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_nlink_t.h b/libc-bottom-half/headers/public/__typedef_nlink_t.h
new file mode 100644
index 0000000..ae34c79
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_nlink_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_nlink_t_h
+#define __wasilibc___typedef_nlink_t_h
+
+/* Define these as 64-bit unsigned integers to support billions of links. */
+typedef unsigned long long nlink_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_off_t.h b/libc-bottom-half/headers/public/__typedef_off_t.h
new file mode 100644
index 0000000..115ffdd
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_off_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_off_t_h
+#define __wasilibc___typedef_off_t_h
+
+/* Define these as 64-bit signed integers to support files larger than 2 GiB. */
+typedef long long off_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_sa_family_t.h b/libc-bottom-half/headers/public/__typedef_sa_family_t.h
new file mode 100644
index 0000000..0ad18c9
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_sa_family_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_sa_family_t_h
+#define __wasilibc___typedef_sa_family_t_h
+
+typedef unsigned short sa_family_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_sigset_t.h b/libc-bottom-half/headers/public/__typedef_sigset_t.h
new file mode 100644
index 0000000..f7e4867
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_sigset_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_sigset_t_h
+#define __wasilibc___typedef_sigset_t_h
+
+/* TODO: This is just a placeholder for now. Keep this in sync with musl. */
+typedef unsigned char sigset_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_socklen_t.h b/libc-bottom-half/headers/public/__typedef_socklen_t.h
new file mode 100644
index 0000000..bd68e55
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_socklen_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_socklen_t_h
+#define __wasilibc___typedef_socklen_t_h
+
+typedef unsigned socklen_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_ssize_t.h b/libc-bottom-half/headers/public/__typedef_ssize_t.h
new file mode 100644
index 0000000..25dc15a
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_ssize_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_ssize_t_h
+#define __wasilibc___typedef_ssize_t_h
+
+/* This is defined to be the same size as size_t. */
+typedef long ssize_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_suseconds_t.h b/libc-bottom-half/headers/public/__typedef_suseconds_t.h
new file mode 100644
index 0000000..92667e2
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_suseconds_t.h
@@ -0,0 +1,8 @@
+#ifndef __wasilibc___typedef_suseconds_t_h
+#define __wasilibc___typedef_suseconds_t_h
+
+/* Define this to be 64-bit as its main use is in struct timeval where the
+ extra space would otherwise be padding. */
+typedef long long suseconds_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_time_t.h b/libc-bottom-half/headers/public/__typedef_time_t.h
new file mode 100644
index 0000000..6ee0f86
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_time_t.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc___typedef_time_t_h
+#define __wasilibc___typedef_time_t_h
+
+/* Define this as a 64-bit signed integer to avoid the 2038 bug. */
+typedef long long time_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/__typedef_uid_t.h b/libc-bottom-half/headers/public/__typedef_uid_t.h
new file mode 100644
index 0000000..c9da300
--- /dev/null
+++ b/libc-bottom-half/headers/public/__typedef_uid_t.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc___typedef_uid_t_h
+#define __wasilibc___typedef_uid_t_h
+
+typedef unsigned uid_t;
+
+#endif
diff --git a/libc-bottom-half/headers/public/dirent.h b/libc-bottom-half/headers/public/dirent.h
new file mode 100644
index 0000000..ce1364a
--- /dev/null
+++ b/libc-bottom-half/headers/public/dirent.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_dirent_h
+#define __wasilibc_dirent_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc dirent implementations.
+ */
+#include <__header_dirent.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/errno.h b/libc-bottom-half/headers/public/errno.h
new file mode 100644
index 0000000..f3914ee
--- /dev/null
+++ b/libc-bottom-half/headers/public/errno.h
@@ -0,0 +1,7 @@
+#ifndef __wasilibc_errno_h
+#define __wasilibc_errno_h
+
+#include <__errno.h>
+#include <__errno_values.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/fcntl.h b/libc-bottom-half/headers/public/fcntl.h
new file mode 100644
index 0000000..0ca3e86
--- /dev/null
+++ b/libc-bottom-half/headers/public/fcntl.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_fcntl_h
+#define __wasilibc_fcntl_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc fcntl implementations.
+ */
+#include <__header_fcntl.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/inttypes.h b/libc-bottom-half/headers/public/inttypes.h
new file mode 100644
index 0000000..a237857
--- /dev/null
+++ b/libc-bottom-half/headers/public/inttypes.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_inttypes_h
+#define __wasilibc_inttypes_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc stdlib implementations.
+ */
+#include <__header_inttypes.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/netinet/in.h b/libc-bottom-half/headers/public/netinet/in.h
new file mode 100644
index 0000000..b27bffe
--- /dev/null
+++ b/libc-bottom-half/headers/public/netinet/in.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc_netinet_in_h
+#define __wasilibc_netinet_in_h
+
+#include <__header_netinet_in.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/poll.h b/libc-bottom-half/headers/public/poll.h
new file mode 100644
index 0000000..22f4b6c
--- /dev/null
+++ b/libc-bottom-half/headers/public/poll.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_poll_h
+#define __wasilibc_poll_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc poll implementations.
+ */
+#include <__header_poll.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/stdlib.h b/libc-bottom-half/headers/public/stdlib.h
new file mode 100644
index 0000000..8425cb8
--- /dev/null
+++ b/libc-bottom-half/headers/public/stdlib.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_stdlib_h
+#define __wasilibc_stdlib_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc stdlib implementations.
+ */
+#include <__functions_malloc.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/string.h b/libc-bottom-half/headers/public/string.h
new file mode 100644
index 0000000..fee51ef
--- /dev/null
+++ b/libc-bottom-half/headers/public/string.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_string_h
+#define __wasilibc_string_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc string implementations.
+ */
+#include <__header_string.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/ioctl.h b/libc-bottom-half/headers/public/sys/ioctl.h
new file mode 100644
index 0000000..085b314
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/ioctl.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_sys_ioctl_h
+#define __wasilibc_sys_ioctl_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc ioctl implementations.
+ */
+#include <__header_sys_ioctl.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/resource.h b/libc-bottom-half/headers/public/sys/resource.h
new file mode 100644
index 0000000..5896861
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/resource.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_sys_resource_h
+#define __wasilibc_sys_resource_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc resource implementations.
+ */
+#include <__header_sys_resource.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/select.h b/libc-bottom-half/headers/public/sys/select.h
new file mode 100644
index 0000000..7570f14
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/select.h
@@ -0,0 +1,18 @@
+#ifndef __wasilibc_sys_select_h
+#define __wasilibc_sys_select_h
+
+#include <__fd_set.h>
+#include <__struct_timespec.h>
+#include <__struct_timeval.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int pselect(int, fd_set *, fd_set *, fd_set *, const struct timespec *, const sigset_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/socket.h b/libc-bottom-half/headers/public/sys/socket.h
new file mode 100644
index 0000000..874f605
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/socket.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc_sys_socket_h
+#define __wasilibc_sys_socket_h
+
+#include <__header_sys_socket.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/stat.h b/libc-bottom-half/headers/public/sys/stat.h
new file mode 100644
index 0000000..586b41f
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/stat.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_sys_stat_h
+#define __wasilibc_sys_stat_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc stat implementations.
+ */
+#include <__header_sys_stat.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/time.h b/libc-bottom-half/headers/public/sys/time.h
new file mode 100644
index 0000000..3ee0b3b
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/time.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc_sys_time_h
+#define __wasilibc_sys_time_h
+
+#include <__struct_timeval.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/times.h b/libc-bottom-half/headers/public/sys/times.h
new file mode 100644
index 0000000..986bc0d
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/times.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc_sys_times_h
+#define __wasilibc_sys_times_h
+
+#include <__struct_tms.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/types.h b/libc-bottom-half/headers/public/sys/types.h
new file mode 100644
index 0000000..3bce96f
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/types.h
@@ -0,0 +1,16 @@
+#ifndef __wasilibc_sys_types_h
+#define __wasilibc_sys_types_h
+
+#define __need_size_t
+#include <stddef.h>
+
+#include <__typedef_clock_t.h>
+#include <__typedef_time_t.h>
+#include <__typedef_blksize_t.h>
+#include <__typedef_off_t.h>
+#include <__typedef_ssize_t.h>
+#include <__typedef_suseconds_t.h>
+#include <__typedef_nlink_t.h>
+#include <__typedef_clockid_t.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/uio.h b/libc-bottom-half/headers/public/sys/uio.h
new file mode 100644
index 0000000..bb502a1
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/uio.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc_sys_uio_h
+#define __wasilibc_sys_uio_h
+
+#include <__struct_iovec.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/sys/un.h b/libc-bottom-half/headers/public/sys/un.h
new file mode 100644
index 0000000..997e613
--- /dev/null
+++ b/libc-bottom-half/headers/public/sys/un.h
@@ -0,0 +1,6 @@
+#ifndef __wasilibc_sys_un_h
+#define __wasilibc_sys_un_h
+
+#include <__struct_sockaddr_un.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/time.h b/libc-bottom-half/headers/public/time.h
new file mode 100644
index 0000000..646e599
--- /dev/null
+++ b/libc-bottom-half/headers/public/time.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_time_h
+#define __wasilibc_time_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc time implementations.
+ */
+#include <__header_time.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/unistd.h b/libc-bottom-half/headers/public/unistd.h
new file mode 100644
index 0000000..5f8edfa
--- /dev/null
+++ b/libc-bottom-half/headers/public/unistd.h
@@ -0,0 +1,10 @@
+#ifndef __wasilibc_unistd_h
+#define __wasilibc_unistd_h
+
+/*
+ * Include the real implementation, which is factored into a separate file so
+ * that it can be reused by other libc unistd implementations.
+ */
+#include <__header_unistd.h>
+
+#endif
diff --git a/libc-bottom-half/headers/public/wasi/api.h b/libc-bottom-half/headers/public/wasi/api.h
new file mode 100644
index 0000000..1ab7699
--- /dev/null
+++ b/libc-bottom-half/headers/public/wasi/api.h
@@ -0,0 +1,2115 @@
+/**
+ * <wasi/api.h>. This file contains declarations describing the WASI ABI
+ * as of "snapshot preview1". It was originally auto-generated from
+ * wasi_snapshot_preview1.witx, however WASI is in the process of
+ * transitioning to a new IDL and header file generator, and this file
+ * is temporarily being manually maintained.
+ *
+ * @file
+ * This file describes the [WASI] interface, consisting of functions, types,
+ * and defined values (macros).
+ *
+ * The interface described here is greatly inspired by [CloudABI]'s clean,
+ * thoughtfully-designed, capability-oriented, POSIX-style API.
+ *
+ * [CloudABI]: https://github.com/NuxiNL/cloudlibc
+ * [WASI]: https://github.com/WebAssembly/WASI/
+ */
+
+#ifndef __wasi_api_h
+#define __wasi_api_h
+
+#ifndef __wasi__
+#error <wasi/api.h> is only supported on WASI platforms.
+#endif
+
+#ifndef __wasm32__
+#error <wasi/api.h> only supports wasm32; doesn't yet support wasm64
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+_Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout");
+_Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout");
+_Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout");
+_Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout");
+_Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout");
+_Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout");
+_Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout");
+_Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout");
+_Static_assert(_Alignof(void*) == 4, "non-wasi data layout");
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// TODO: Encoding this in witx.
+#define __WASI_DIRCOOKIE_START (UINT64_C(0))
+typedef __SIZE_TYPE__ __wasi_size_t;
+
+_Static_assert(sizeof(__wasi_size_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_size_t) == 4, "witx calculated align");
+
+/**
+ * Non-negative file size or length of a region within a file.
+ */
+typedef uint64_t __wasi_filesize_t;
+
+_Static_assert(sizeof(__wasi_filesize_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_filesize_t) == 8, "witx calculated align");
+
+/**
+ * Timestamp in nanoseconds.
+ */
+typedef uint64_t __wasi_timestamp_t;
+
+_Static_assert(sizeof(__wasi_timestamp_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_timestamp_t) == 8, "witx calculated align");
+
+/**
+ * Identifiers for clocks.
+ */
+typedef uint32_t __wasi_clockid_t;
+
+/**
+ * The clock measuring real time. Time value zero corresponds with
+ * 1970-01-01T00:00:00Z.
+ */
+#define __WASI_CLOCKID_REALTIME (UINT32_C(0))
+
+/**
+ * The store-wide monotonic clock, which is defined as a clock measuring
+ * real time, whose value cannot be adjusted and which cannot have negative
+ * clock jumps. The epoch of this clock is undefined. The absolute time
+ * value of this clock therefore has no meaning.
+ */
+#define __WASI_CLOCKID_MONOTONIC (UINT32_C(1))
+
+/**
+ * The CPU-time clock associated with the current process.
+ */
+#define __WASI_CLOCKID_PROCESS_CPUTIME_ID (UINT32_C(2))
+
+/**
+ * The CPU-time clock associated with the current thread.
+ */
+#define __WASI_CLOCKID_THREAD_CPUTIME_ID (UINT32_C(3))
+
+_Static_assert(sizeof(__wasi_clockid_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_clockid_t) == 4, "witx calculated align");
+
+/**
+ * Error codes returned by functions.
+ * Not all of these error codes are returned by the functions provided by this
+ * API; some are used in higher-level library layers, and others are provided
+ * merely for alignment with POSIX.
+ */
+typedef uint16_t __wasi_errno_t;
+
+/**
+ * No error occurred. System call completed successfully.
+ */
+#define __WASI_ERRNO_SUCCESS (UINT16_C(0))
+
+/**
+ * Argument list too long.
+ */
+#define __WASI_ERRNO_2BIG (UINT16_C(1))
+
+/**
+ * Permission denied.
+ */
+#define __WASI_ERRNO_ACCES (UINT16_C(2))
+
+/**
+ * Address in use.
+ */
+#define __WASI_ERRNO_ADDRINUSE (UINT16_C(3))
+
+/**
+ * Address not available.
+ */
+#define __WASI_ERRNO_ADDRNOTAVAIL (UINT16_C(4))
+
+/**
+ * Address family not supported.
+ */
+#define __WASI_ERRNO_AFNOSUPPORT (UINT16_C(5))
+
+/**
+ * Resource unavailable, or operation would block.
+ */
+#define __WASI_ERRNO_AGAIN (UINT16_C(6))
+
+/**
+ * Connection already in progress.
+ */
+#define __WASI_ERRNO_ALREADY (UINT16_C(7))
+
+/**
+ * Bad file descriptor.
+ */
+#define __WASI_ERRNO_BADF (UINT16_C(8))
+
+/**
+ * Bad message.
+ */
+#define __WASI_ERRNO_BADMSG (UINT16_C(9))
+
+/**
+ * Device or resource busy.
+ */
+#define __WASI_ERRNO_BUSY (UINT16_C(10))
+
+/**
+ * Operation canceled.
+ */
+#define __WASI_ERRNO_CANCELED (UINT16_C(11))
+
+/**
+ * No child processes.
+ */
+#define __WASI_ERRNO_CHILD (UINT16_C(12))
+
+/**
+ * Connection aborted.
+ */
+#define __WASI_ERRNO_CONNABORTED (UINT16_C(13))
+
+/**
+ * Connection refused.
+ */
+#define __WASI_ERRNO_CONNREFUSED (UINT16_C(14))
+
+/**
+ * Connection reset.
+ */
+#define __WASI_ERRNO_CONNRESET (UINT16_C(15))
+
+/**
+ * Resource deadlock would occur.
+ */
+#define __WASI_ERRNO_DEADLK (UINT16_C(16))
+
+/**
+ * Destination address required.
+ */
+#define __WASI_ERRNO_DESTADDRREQ (UINT16_C(17))
+
+/**
+ * Mathematics argument out of domain of function.
+ */
+#define __WASI_ERRNO_DOM (UINT16_C(18))
+
+/**
+ * Reserved.
+ */
+#define __WASI_ERRNO_DQUOT (UINT16_C(19))
+
+/**
+ * File exists.
+ */
+#define __WASI_ERRNO_EXIST (UINT16_C(20))
+
+/**
+ * Bad address.
+ */
+#define __WASI_ERRNO_FAULT (UINT16_C(21))
+
+/**
+ * File too large.
+ */
+#define __WASI_ERRNO_FBIG (UINT16_C(22))
+
+/**
+ * Host is unreachable.
+ */
+#define __WASI_ERRNO_HOSTUNREACH (UINT16_C(23))
+
+/**
+ * Identifier removed.
+ */
+#define __WASI_ERRNO_IDRM (UINT16_C(24))
+
+/**
+ * Illegal byte sequence.
+ */
+#define __WASI_ERRNO_ILSEQ (UINT16_C(25))
+
+/**
+ * Operation in progress.
+ */
+#define __WASI_ERRNO_INPROGRESS (UINT16_C(26))
+
+/**
+ * Interrupted function.
+ */
+#define __WASI_ERRNO_INTR (UINT16_C(27))
+
+/**
+ * Invalid argument.
+ */
+#define __WASI_ERRNO_INVAL (UINT16_C(28))
+
+/**
+ * I/O error.
+ */
+#define __WASI_ERRNO_IO (UINT16_C(29))
+
+/**
+ * Socket is connected.
+ */
+#define __WASI_ERRNO_ISCONN (UINT16_C(30))
+
+/**
+ * Is a directory.
+ */
+#define __WASI_ERRNO_ISDIR (UINT16_C(31))
+
+/**
+ * Too many levels of symbolic links.
+ */
+#define __WASI_ERRNO_LOOP (UINT16_C(32))
+
+/**
+ * File descriptor value too large.
+ */
+#define __WASI_ERRNO_MFILE (UINT16_C(33))
+
+/**
+ * Too many links.
+ */
+#define __WASI_ERRNO_MLINK (UINT16_C(34))
+
+/**
+ * Message too large.
+ */
+#define __WASI_ERRNO_MSGSIZE (UINT16_C(35))
+
+/**
+ * Reserved.
+ */
+#define __WASI_ERRNO_MULTIHOP (UINT16_C(36))
+
+/**
+ * Filename too long.
+ */
+#define __WASI_ERRNO_NAMETOOLONG (UINT16_C(37))
+
+/**
+ * Network is down.
+ */
+#define __WASI_ERRNO_NETDOWN (UINT16_C(38))
+
+/**
+ * Connection aborted by network.
+ */
+#define __WASI_ERRNO_NETRESET (UINT16_C(39))
+
+/**
+ * Network unreachable.
+ */
+#define __WASI_ERRNO_NETUNREACH (UINT16_C(40))
+
+/**
+ * Too many files open in system.
+ */
+#define __WASI_ERRNO_NFILE (UINT16_C(41))
+
+/**
+ * No buffer space available.
+ */
+#define __WASI_ERRNO_NOBUFS (UINT16_C(42))
+
+/**
+ * No such device.
+ */
+#define __WASI_ERRNO_NODEV (UINT16_C(43))
+
+/**
+ * No such file or directory.
+ */
+#define __WASI_ERRNO_NOENT (UINT16_C(44))
+
+/**
+ * Executable file format error.
+ */
+#define __WASI_ERRNO_NOEXEC (UINT16_C(45))
+
+/**
+ * No locks available.
+ */
+#define __WASI_ERRNO_NOLCK (UINT16_C(46))
+
+/**
+ * Reserved.
+ */
+#define __WASI_ERRNO_NOLINK (UINT16_C(47))
+
+/**
+ * Not enough space.
+ */
+#define __WASI_ERRNO_NOMEM (UINT16_C(48))
+
+/**
+ * No message of the desired type.
+ */
+#define __WASI_ERRNO_NOMSG (UINT16_C(49))
+
+/**
+ * Protocol not available.
+ */
+#define __WASI_ERRNO_NOPROTOOPT (UINT16_C(50))
+
+/**
+ * No space left on device.
+ */
+#define __WASI_ERRNO_NOSPC (UINT16_C(51))
+
+/**
+ * Function not supported.
+ */
+#define __WASI_ERRNO_NOSYS (UINT16_C(52))
+
+/**
+ * The socket is not connected.
+ */
+#define __WASI_ERRNO_NOTCONN (UINT16_C(53))
+
+/**
+ * Not a directory or a symbolic link to a directory.
+ */
+#define __WASI_ERRNO_NOTDIR (UINT16_C(54))
+
+/**
+ * Directory not empty.
+ */
+#define __WASI_ERRNO_NOTEMPTY (UINT16_C(55))
+
+/**
+ * State not recoverable.
+ */
+#define __WASI_ERRNO_NOTRECOVERABLE (UINT16_C(56))
+
+/**
+ * Not a socket.
+ */
+#define __WASI_ERRNO_NOTSOCK (UINT16_C(57))
+
+/**
+ * Not supported, or operation not supported on socket.
+ */
+#define __WASI_ERRNO_NOTSUP (UINT16_C(58))
+
+/**
+ * Inappropriate I/O control operation.
+ */
+#define __WASI_ERRNO_NOTTY (UINT16_C(59))
+
+/**
+ * No such device or address.
+ */
+#define __WASI_ERRNO_NXIO (UINT16_C(60))
+
+/**
+ * Value too large to be stored in data type.
+ */
+#define __WASI_ERRNO_OVERFLOW (UINT16_C(61))
+
+/**
+ * Previous owner died.
+ */
+#define __WASI_ERRNO_OWNERDEAD (UINT16_C(62))
+
+/**
+ * Operation not permitted.
+ */
+#define __WASI_ERRNO_PERM (UINT16_C(63))
+
+/**
+ * Broken pipe.
+ */
+#define __WASI_ERRNO_PIPE (UINT16_C(64))
+
+/**
+ * Protocol error.
+ */
+#define __WASI_ERRNO_PROTO (UINT16_C(65))
+
+/**
+ * Protocol not supported.
+ */
+#define __WASI_ERRNO_PROTONOSUPPORT (UINT16_C(66))
+
+/**
+ * Protocol wrong type for socket.
+ */
+#define __WASI_ERRNO_PROTOTYPE (UINT16_C(67))
+
+/**
+ * Result too large.
+ */
+#define __WASI_ERRNO_RANGE (UINT16_C(68))
+
+/**
+ * Read-only file system.
+ */
+#define __WASI_ERRNO_ROFS (UINT16_C(69))
+
+/**
+ * Invalid seek.
+ */
+#define __WASI_ERRNO_SPIPE (UINT16_C(70))
+
+/**
+ * No such process.
+ */
+#define __WASI_ERRNO_SRCH (UINT16_C(71))
+
+/**
+ * Reserved.
+ */
+#define __WASI_ERRNO_STALE (UINT16_C(72))
+
+/**
+ * Connection timed out.
+ */
+#define __WASI_ERRNO_TIMEDOUT (UINT16_C(73))
+
+/**
+ * Text file busy.
+ */
+#define __WASI_ERRNO_TXTBSY (UINT16_C(74))
+
+/**
+ * Cross-device link.
+ */
+#define __WASI_ERRNO_XDEV (UINT16_C(75))
+
+/**
+ * Extension: Capabilities insufficient.
+ */
+#define __WASI_ERRNO_NOTCAPABLE (UINT16_C(76))
+
+_Static_assert(sizeof(__wasi_errno_t) == 2, "witx calculated size");
+_Static_assert(_Alignof(__wasi_errno_t) == 2, "witx calculated align");
+
+/**
+ * File descriptor rights, determining which actions may be performed.
+ */
+typedef uint64_t __wasi_rights_t;
+
+/**
+ * The right to invoke `fd_datasync`.
+ * If `path_open` is set, includes the right to invoke
+ * `path_open` with `fdflags::dsync`.
+ */
+#define __WASI_RIGHTS_FD_DATASYNC ((__wasi_rights_t)(1 << 0))
+
+/**
+ * The right to invoke `fd_read` and `sock_recv`.
+ * If `rights::fd_seek` is set, includes the right to invoke `fd_pread`.
+ */
+#define __WASI_RIGHTS_FD_READ ((__wasi_rights_t)(1 << 1))
+
+/**
+ * The right to invoke `fd_seek`. This flag implies `rights::fd_tell`.
+ */
+#define __WASI_RIGHTS_FD_SEEK ((__wasi_rights_t)(1 << 2))
+
+/**
+ * The right to invoke `fd_fdstat_set_flags`.
+ */
+#define __WASI_RIGHTS_FD_FDSTAT_SET_FLAGS ((__wasi_rights_t)(1 << 3))
+
+/**
+ * The right to invoke `fd_sync`.
+ * If `path_open` is set, includes the right to invoke
+ * `path_open` with `fdflags::rsync` and `fdflags::dsync`.
+ */
+#define __WASI_RIGHTS_FD_SYNC ((__wasi_rights_t)(1 << 4))
+
+/**
+ * The right to invoke `fd_seek` in such a way that the file offset
+ * remains unaltered (i.e., `whence::cur` with offset zero), or to
+ * invoke `fd_tell`.
+ */
+#define __WASI_RIGHTS_FD_TELL ((__wasi_rights_t)(1 << 5))
+
+/**
+ * The right to invoke `fd_write` and `sock_send`.
+ * If `rights::fd_seek` is set, includes the right to invoke `fd_pwrite`.
+ */
+#define __WASI_RIGHTS_FD_WRITE ((__wasi_rights_t)(1 << 6))
+
+/**
+ * The right to invoke `fd_advise`.
+ */
+#define __WASI_RIGHTS_FD_ADVISE ((__wasi_rights_t)(1 << 7))
+
+/**
+ * The right to invoke `fd_allocate`.
+ */
+#define __WASI_RIGHTS_FD_ALLOCATE ((__wasi_rights_t)(1 << 8))
+
+/**
+ * The right to invoke `path_create_directory`.
+ */
+#define __WASI_RIGHTS_PATH_CREATE_DIRECTORY ((__wasi_rights_t)(1 << 9))
+
+/**
+ * If `path_open` is set, the right to invoke `path_open` with `oflags::creat`.
+ */
+#define __WASI_RIGHTS_PATH_CREATE_FILE ((__wasi_rights_t)(1 << 10))
+
+/**
+ * The right to invoke `path_link` with the file descriptor as the
+ * source directory.
+ */
+#define __WASI_RIGHTS_PATH_LINK_SOURCE ((__wasi_rights_t)(1 << 11))
+
+/**
+ * The right to invoke `path_link` with the file descriptor as the
+ * target directory.
+ */
+#define __WASI_RIGHTS_PATH_LINK_TARGET ((__wasi_rights_t)(1 << 12))
+
+/**
+ * The right to invoke `path_open`.
+ */
+#define __WASI_RIGHTS_PATH_OPEN ((__wasi_rights_t)(1 << 13))
+
+/**
+ * The right to invoke `fd_readdir`.
+ */
+#define __WASI_RIGHTS_FD_READDIR ((__wasi_rights_t)(1 << 14))
+
+/**
+ * The right to invoke `path_readlink`.
+ */
+#define __WASI_RIGHTS_PATH_READLINK ((__wasi_rights_t)(1 << 15))
+
+/**
+ * The right to invoke `path_rename` with the file descriptor as the source directory.
+ */
+#define __WASI_RIGHTS_PATH_RENAME_SOURCE ((__wasi_rights_t)(1 << 16))
+
+/**
+ * The right to invoke `path_rename` with the file descriptor as the target directory.
+ */
+#define __WASI_RIGHTS_PATH_RENAME_TARGET ((__wasi_rights_t)(1 << 17))
+
+/**
+ * The right to invoke `path_filestat_get`.
+ */
+#define __WASI_RIGHTS_PATH_FILESTAT_GET ((__wasi_rights_t)(1 << 18))
+
+/**
+ * The right to change a file's size (there is no `path_filestat_set_size`).
+ * If `path_open` is set, includes the right to invoke `path_open` with `oflags::trunc`.
+ */
+#define __WASI_RIGHTS_PATH_FILESTAT_SET_SIZE ((__wasi_rights_t)(1 << 19))
+
+/**
+ * The right to invoke `path_filestat_set_times`.
+ */
+#define __WASI_RIGHTS_PATH_FILESTAT_SET_TIMES ((__wasi_rights_t)(1 << 20))
+
+/**
+ * The right to invoke `fd_filestat_get`.
+ */
+#define __WASI_RIGHTS_FD_FILESTAT_GET ((__wasi_rights_t)(1 << 21))
+
+/**
+ * The right to invoke `fd_filestat_set_size`.
+ */
+#define __WASI_RIGHTS_FD_FILESTAT_SET_SIZE ((__wasi_rights_t)(1 << 22))
+
+/**
+ * The right to invoke `fd_filestat_set_times`.
+ */
+#define __WASI_RIGHTS_FD_FILESTAT_SET_TIMES ((__wasi_rights_t)(1 << 23))
+
+/**
+ * The right to invoke `path_symlink`.
+ */
+#define __WASI_RIGHTS_PATH_SYMLINK ((__wasi_rights_t)(1 << 24))
+
+/**
+ * The right to invoke `path_remove_directory`.
+ */
+#define __WASI_RIGHTS_PATH_REMOVE_DIRECTORY ((__wasi_rights_t)(1 << 25))
+
+/**
+ * The right to invoke `path_unlink_file`.
+ */
+#define __WASI_RIGHTS_PATH_UNLINK_FILE ((__wasi_rights_t)(1 << 26))
+
+/**
+ * If `rights::fd_read` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_read`.
+ * If `rights::fd_write` is set, includes the right to invoke `poll_oneoff` to subscribe to `eventtype::fd_write`.
+ */
+#define __WASI_RIGHTS_POLL_FD_READWRITE ((__wasi_rights_t)(1 << 27))
+
+/**
+ * The right to invoke `sock_shutdown`.
+ */
+#define __WASI_RIGHTS_SOCK_SHUTDOWN ((__wasi_rights_t)(1 << 28))
+
+/**
+ * The right to invoke `sock_accept`.
+ */
+#define __WASI_RIGHTS_SOCK_ACCEPT ((__wasi_rights_t)(1 << 29))
+
+/**
+ * A file descriptor handle.
+ */
+typedef int __wasi_fd_t;
+
+_Static_assert(sizeof(__wasi_fd_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_fd_t) == 4, "witx calculated align");
+
+/**
+ * A region of memory for scatter/gather reads.
+ */
+typedef struct __wasi_iovec_t {
+ /**
+ * The address of the buffer to be filled.
+ */
+ uint8_t * buf;
+
+ /**
+ * The length of the buffer to be filled.
+ */
+ __wasi_size_t buf_len;
+
+} __wasi_iovec_t;
+
+_Static_assert(sizeof(__wasi_iovec_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_iovec_t) == 4, "witx calculated align");
+_Static_assert(offsetof(__wasi_iovec_t, buf) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_iovec_t, buf_len) == 4, "witx calculated offset");
+
+/**
+ * A region of memory for scatter/gather writes.
+ */
+typedef struct __wasi_ciovec_t {
+ /**
+ * The address of the buffer to be written.
+ */
+ const uint8_t * buf;
+
+ /**
+ * The length of the buffer to be written.
+ */
+ __wasi_size_t buf_len;
+
+} __wasi_ciovec_t;
+
+_Static_assert(sizeof(__wasi_ciovec_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_ciovec_t) == 4, "witx calculated align");
+_Static_assert(offsetof(__wasi_ciovec_t, buf) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_ciovec_t, buf_len) == 4, "witx calculated offset");
+
+/**
+ * Relative offset within a file.
+ */
+typedef int64_t __wasi_filedelta_t;
+
+_Static_assert(sizeof(__wasi_filedelta_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_filedelta_t) == 8, "witx calculated align");
+
+/**
+ * The position relative to which to set the offset of the file descriptor.
+ */
+typedef uint8_t __wasi_whence_t;
+
+/**
+ * Seek relative to start-of-file.
+ */
+#define __WASI_WHENCE_SET (UINT8_C(0))
+
+/**
+ * Seek relative to current position.
+ */
+#define __WASI_WHENCE_CUR (UINT8_C(1))
+
+/**
+ * Seek relative to end-of-file.
+ */
+#define __WASI_WHENCE_END (UINT8_C(2))
+
+_Static_assert(sizeof(__wasi_whence_t) == 1, "witx calculated size");
+_Static_assert(_Alignof(__wasi_whence_t) == 1, "witx calculated align");
+
+/**
+ * A reference to the offset of a directory entry.
+ *
+ * The value 0 signifies the start of the directory.
+ */
+typedef uint64_t __wasi_dircookie_t;
+
+_Static_assert(sizeof(__wasi_dircookie_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_dircookie_t) == 8, "witx calculated align");
+
+/**
+ * The type for the `dirent::d_namlen` field of `dirent` struct.
+ */
+typedef uint32_t __wasi_dirnamlen_t;
+
+_Static_assert(sizeof(__wasi_dirnamlen_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_dirnamlen_t) == 4, "witx calculated align");
+
+/**
+ * File serial number that is unique within its file system.
+ */
+typedef uint64_t __wasi_inode_t;
+
+_Static_assert(sizeof(__wasi_inode_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_inode_t) == 8, "witx calculated align");
+
+/**
+ * The type of a file descriptor or file.
+ */
+typedef uint8_t __wasi_filetype_t;
+
+/**
+ * The type of the file descriptor or file is unknown or is different from any of the other types specified.
+ */
+#define __WASI_FILETYPE_UNKNOWN (UINT8_C(0))
+
+/**
+ * The file descriptor or file refers to a block device inode.
+ */
+#define __WASI_FILETYPE_BLOCK_DEVICE (UINT8_C(1))
+
+/**
+ * The file descriptor or file refers to a character device inode.
+ */
+#define __WASI_FILETYPE_CHARACTER_DEVICE (UINT8_C(2))
+
+/**
+ * The file descriptor or file refers to a directory inode.
+ */
+#define __WASI_FILETYPE_DIRECTORY (UINT8_C(3))
+
+/**
+ * The file descriptor or file refers to a regular file inode.
+ */
+#define __WASI_FILETYPE_REGULAR_FILE (UINT8_C(4))
+
+/**
+ * The file descriptor or file refers to a datagram socket.
+ */
+#define __WASI_FILETYPE_SOCKET_DGRAM (UINT8_C(5))
+
+/**
+ * The file descriptor or file refers to a byte-stream socket.
+ */
+#define __WASI_FILETYPE_SOCKET_STREAM (UINT8_C(6))
+
+/**
+ * The file refers to a symbolic link inode.
+ */
+#define __WASI_FILETYPE_SYMBOLIC_LINK (UINT8_C(7))
+
+_Static_assert(sizeof(__wasi_filetype_t) == 1, "witx calculated size");
+_Static_assert(_Alignof(__wasi_filetype_t) == 1, "witx calculated align");
+
+/**
+ * A directory entry.
+ */
+typedef struct __wasi_dirent_t {
+ /**
+ * The offset of the next directory entry stored in this directory.
+ */
+ __wasi_dircookie_t d_next;
+
+ /**
+ * The serial number of the file referred to by this directory entry.
+ */
+ __wasi_inode_t d_ino;
+
+ /**
+ * The length of the name of the directory entry.
+ */
+ __wasi_dirnamlen_t d_namlen;
+
+ /**
+ * The type of the file referred to by this directory entry.
+ */
+ __wasi_filetype_t d_type;
+
+} __wasi_dirent_t;
+
+_Static_assert(sizeof(__wasi_dirent_t) == 24, "witx calculated size");
+_Static_assert(_Alignof(__wasi_dirent_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_dirent_t, d_next) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_dirent_t, d_ino) == 8, "witx calculated offset");
+_Static_assert(offsetof(__wasi_dirent_t, d_namlen) == 16, "witx calculated offset");
+_Static_assert(offsetof(__wasi_dirent_t, d_type) == 20, "witx calculated offset");
+
+/**
+ * File or memory access pattern advisory information.
+ */
+typedef uint8_t __wasi_advice_t;
+
+/**
+ * The application has no advice to give on its behavior with respect to the specified data.
+ */
+#define __WASI_ADVICE_NORMAL (UINT8_C(0))
+
+/**
+ * The application expects to access the specified data sequentially from lower offsets to higher offsets.
+ */
+#define __WASI_ADVICE_SEQUENTIAL (UINT8_C(1))
+
+/**
+ * The application expects to access the specified data in a random order.
+ */
+#define __WASI_ADVICE_RANDOM (UINT8_C(2))
+
+/**
+ * The application expects to access the specified data in the near future.
+ */
+#define __WASI_ADVICE_WILLNEED (UINT8_C(3))
+
+/**
+ * The application expects that it will not access the specified data in the near future.
+ */
+#define __WASI_ADVICE_DONTNEED (UINT8_C(4))
+
+/**
+ * The application expects to access the specified data once and then not reuse it thereafter.
+ */
+#define __WASI_ADVICE_NOREUSE (UINT8_C(5))
+
+_Static_assert(sizeof(__wasi_advice_t) == 1, "witx calculated size");
+_Static_assert(_Alignof(__wasi_advice_t) == 1, "witx calculated align");
+
+/**
+ * File descriptor flags.
+ */
+typedef uint16_t __wasi_fdflags_t;
+
+/**
+ * Append mode: Data written to the file is always appended to the file's end.
+ */
+#define __WASI_FDFLAGS_APPEND ((__wasi_fdflags_t)(1 << 0))
+
+/**
+ * Write according to synchronized I/O data integrity completion. Only the data stored in the file is synchronized.
+ */
+#define __WASI_FDFLAGS_DSYNC ((__wasi_fdflags_t)(1 << 1))
+
+/**
+ * Non-blocking mode.
+ */
+#define __WASI_FDFLAGS_NONBLOCK ((__wasi_fdflags_t)(1 << 2))
+
+/**
+ * Synchronized read I/O operations.
+ */
+#define __WASI_FDFLAGS_RSYNC ((__wasi_fdflags_t)(1 << 3))
+
+/**
+ * Write according to synchronized I/O file integrity completion. In
+ * addition to synchronizing the data stored in the file, the implementation
+ * may also synchronously update the file's metadata.
+ */
+#define __WASI_FDFLAGS_SYNC ((__wasi_fdflags_t)(1 << 4))
+
+/**
+ * File descriptor attributes.
+ */
+typedef struct __wasi_fdstat_t {
+ /**
+ * File type.
+ */
+ __wasi_filetype_t fs_filetype;
+
+ /**
+ * File descriptor flags.
+ */
+ __wasi_fdflags_t fs_flags;
+
+ /**
+ * Rights that apply to this file descriptor.
+ */
+ __wasi_rights_t fs_rights_base;
+
+ /**
+ * Maximum set of rights that may be installed on new file descriptors that
+ * are created through this file descriptor, e.g., through `path_open`.
+ */
+ __wasi_rights_t fs_rights_inheriting;
+
+} __wasi_fdstat_t;
+
+_Static_assert(sizeof(__wasi_fdstat_t) == 24, "witx calculated size");
+_Static_assert(_Alignof(__wasi_fdstat_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_filetype) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2, "witx calculated offset");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_rights_base) == 8, "witx calculated offset");
+_Static_assert(offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, "witx calculated offset");
+
+/**
+ * Identifier for a device containing a file system. Can be used in combination
+ * with `inode` to uniquely identify a file or directory in the filesystem.
+ */
+typedef uint64_t __wasi_device_t;
+
+_Static_assert(sizeof(__wasi_device_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_device_t) == 8, "witx calculated align");
+
+/**
+ * Which file time attributes to adjust.
+ */
+typedef uint16_t __wasi_fstflags_t;
+
+/**
+ * Adjust the last data access timestamp to the value stored in `filestat::atim`.
+ */
+#define __WASI_FSTFLAGS_ATIM ((__wasi_fstflags_t)(1 << 0))
+
+/**
+ * Adjust the last data access timestamp to the time of clock `clockid::realtime`.
+ */
+#define __WASI_FSTFLAGS_ATIM_NOW ((__wasi_fstflags_t)(1 << 1))
+
+/**
+ * Adjust the last data modification timestamp to the value stored in `filestat::mtim`.
+ */
+#define __WASI_FSTFLAGS_MTIM ((__wasi_fstflags_t)(1 << 2))
+
+/**
+ * Adjust the last data modification timestamp to the time of clock `clockid::realtime`.
+ */
+#define __WASI_FSTFLAGS_MTIM_NOW ((__wasi_fstflags_t)(1 << 3))
+
+/**
+ * Flags determining the method of how paths are resolved.
+ */
+typedef uint32_t __wasi_lookupflags_t;
+
+/**
+ * As long as the resolved path corresponds to a symbolic link, it is expanded.
+ */
+#define __WASI_LOOKUPFLAGS_SYMLINK_FOLLOW ((__wasi_lookupflags_t)(1 << 0))
+
+/**
+ * Open flags used by `path_open`.
+ */
+typedef uint16_t __wasi_oflags_t;
+
+/**
+ * Create file if it does not exist.
+ */
+#define __WASI_OFLAGS_CREAT ((__wasi_oflags_t)(1 << 0))
+
+/**
+ * Fail if not a directory.
+ */
+#define __WASI_OFLAGS_DIRECTORY ((__wasi_oflags_t)(1 << 1))
+
+/**
+ * Fail if file already exists.
+ */
+#define __WASI_OFLAGS_EXCL ((__wasi_oflags_t)(1 << 2))
+
+/**
+ * Truncate file to size 0.
+ */
+#define __WASI_OFLAGS_TRUNC ((__wasi_oflags_t)(1 << 3))
+
+/**
+ * Number of hard links to an inode.
+ */
+typedef uint64_t __wasi_linkcount_t;
+
+_Static_assert(sizeof(__wasi_linkcount_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_linkcount_t) == 8, "witx calculated align");
+
+/**
+ * File attributes.
+ */
+typedef struct __wasi_filestat_t {
+ /**
+ * Device ID of device containing the file.
+ */
+ __wasi_device_t dev;
+
+ /**
+ * File serial number.
+ */
+ __wasi_inode_t ino;
+
+ /**
+ * File type.
+ */
+ __wasi_filetype_t filetype;
+
+ /**
+ * Number of hard links to the file.
+ */
+ __wasi_linkcount_t nlink;
+
+ /**
+ * For regular files, the file size in bytes. For symbolic links, the length in bytes of the pathname contained in the symbolic link.
+ */
+ __wasi_filesize_t size;
+
+ /**
+ * Last data access timestamp.
+ */
+ __wasi_timestamp_t atim;
+
+ /**
+ * Last data modification timestamp.
+ */
+ __wasi_timestamp_t mtim;
+
+ /**
+ * Last file status change timestamp.
+ */
+ __wasi_timestamp_t ctim;
+
+} __wasi_filestat_t;
+
+_Static_assert(sizeof(__wasi_filestat_t) == 64, "witx calculated size");
+_Static_assert(_Alignof(__wasi_filestat_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_filestat_t, dev) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, ino) == 8, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, filetype) == 16, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, nlink) == 24, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, size) == 32, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, atim) == 40, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, mtim) == 48, "witx calculated offset");
+_Static_assert(offsetof(__wasi_filestat_t, ctim) == 56, "witx calculated offset");
+
+/**
+ * User-provided value that may be attached to objects that is retained when
+ * extracted from the implementation.
+ */
+typedef uint64_t __wasi_userdata_t;
+
+_Static_assert(sizeof(__wasi_userdata_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_userdata_t) == 8, "witx calculated align");
+
+/**
+ * Type of a subscription to an event or its occurrence.
+ */
+typedef uint8_t __wasi_eventtype_t;
+
+/**
+ * The time value of clock `subscription_clock::id` has
+ * reached timestamp `subscription_clock::timeout`.
+ */
+#define __WASI_EVENTTYPE_CLOCK (UINT8_C(0))
+
+/**
+ * File descriptor `subscription_fd_readwrite::file_descriptor` has data
+ * available for reading. This event always triggers for regular files.
+ */
+#define __WASI_EVENTTYPE_FD_READ (UINT8_C(1))
+
+/**
+ * File descriptor `subscription_fd_readwrite::file_descriptor` has capacity
+ * available for writing. This event always triggers for regular files.
+ */
+#define __WASI_EVENTTYPE_FD_WRITE (UINT8_C(2))
+
+_Static_assert(sizeof(__wasi_eventtype_t) == 1, "witx calculated size");
+_Static_assert(_Alignof(__wasi_eventtype_t) == 1, "witx calculated align");
+
+/**
+ * The state of the file descriptor subscribed to with
+ * `eventtype::fd_read` or `eventtype::fd_write`.
+ */
+typedef uint16_t __wasi_eventrwflags_t;
+
+/**
+ * The peer of this socket has closed or disconnected.
+ */
+#define __WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP ((__wasi_eventrwflags_t)(1 << 0))
+
+/**
+ * The contents of an `event` when type is `eventtype::fd_read` or
+ * `eventtype::fd_write`.
+ */
+typedef struct __wasi_event_fd_readwrite_t {
+ /**
+ * The number of bytes available for reading or writing.
+ */
+ __wasi_filesize_t nbytes;
+
+ /**
+ * The state of the file descriptor.
+ */
+ __wasi_eventrwflags_t flags;
+
+} __wasi_event_fd_readwrite_t;
+
+_Static_assert(sizeof(__wasi_event_fd_readwrite_t) == 16, "witx calculated size");
+_Static_assert(_Alignof(__wasi_event_fd_readwrite_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_event_fd_readwrite_t, nbytes) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_event_fd_readwrite_t, flags) == 8, "witx calculated offset");
+
+/**
+ * An event that occurred.
+ */
+typedef struct __wasi_event_t {
+ /**
+ * User-provided value that got attached to `subscription::userdata`.
+ */
+ __wasi_userdata_t userdata;
+
+ /**
+ * If non-zero, an error that occurred while processing the subscription request.
+ */
+ __wasi_errno_t error;
+
+ /**
+ * The type of event that occured
+ */
+ __wasi_eventtype_t type;
+
+ /**
+ * The contents of the event, if it is an `eventtype::fd_read` or
+ * `eventtype::fd_write`. `eventtype::clock` events ignore this field.
+ */
+ __wasi_event_fd_readwrite_t fd_readwrite;
+
+} __wasi_event_t;
+
+_Static_assert(sizeof(__wasi_event_t) == 32, "witx calculated size");
+_Static_assert(_Alignof(__wasi_event_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_event_t, userdata) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_event_t, error) == 8, "witx calculated offset");
+_Static_assert(offsetof(__wasi_event_t, type) == 10, "witx calculated offset");
+_Static_assert(offsetof(__wasi_event_t, fd_readwrite) == 16, "witx calculated offset");
+
+/**
+ * Flags determining how to interpret the timestamp provided in
+ * `subscription_clock::timeout`.
+ */
+typedef uint16_t __wasi_subclockflags_t;
+
+/**
+ * If set, treat the timestamp provided in
+ * `subscription_clock::timeout` as an absolute timestamp of clock
+ * `subscription_clock::id`. If clear, treat the timestamp
+ * provided in `subscription_clock::timeout` relative to the
+ * current time value of clock `subscription_clock::id`.
+ */
+#define __WASI_SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME ((__wasi_subclockflags_t)(1 << 0))
+
+/**
+ * The contents of a `subscription` when type is `eventtype::clock`.
+ */
+typedef struct __wasi_subscription_clock_t {
+ /**
+ * The clock against which to compare the timestamp.
+ */
+ __wasi_clockid_t id;
+
+ /**
+ * The absolute or relative timestamp.
+ */
+ __wasi_timestamp_t timeout;
+
+ /**
+ * The amount of time that the implementation may wait additionally
+ * to coalesce with other events.
+ */
+ __wasi_timestamp_t precision;
+
+ /**
+ * Flags specifying whether the timeout is absolute or relative
+ */
+ __wasi_subclockflags_t flags;
+
+} __wasi_subscription_clock_t;
+
+_Static_assert(sizeof(__wasi_subscription_clock_t) == 32, "witx calculated size");
+_Static_assert(_Alignof(__wasi_subscription_clock_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_subscription_clock_t, id) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_subscription_clock_t, timeout) == 8, "witx calculated offset");
+_Static_assert(offsetof(__wasi_subscription_clock_t, precision) == 16, "witx calculated offset");
+_Static_assert(offsetof(__wasi_subscription_clock_t, flags) == 24, "witx calculated offset");
+
+/**
+ * The contents of a `subscription` when type is type is
+ * `eventtype::fd_read` or `eventtype::fd_write`.
+ */
+typedef struct __wasi_subscription_fd_readwrite_t {
+ /**
+ * The file descriptor on which to wait for it to become ready for reading or writing.
+ */
+ __wasi_fd_t file_descriptor;
+
+} __wasi_subscription_fd_readwrite_t;
+
+_Static_assert(sizeof(__wasi_subscription_fd_readwrite_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_subscription_fd_readwrite_t) == 4, "witx calculated align");
+_Static_assert(offsetof(__wasi_subscription_fd_readwrite_t, file_descriptor) == 0, "witx calculated offset");
+
+/**
+ * The contents of a `subscription`.
+ */
+typedef union __wasi_subscription_u_u_t {
+ __wasi_subscription_clock_t clock;
+ __wasi_subscription_fd_readwrite_t fd_read;
+ __wasi_subscription_fd_readwrite_t fd_write;
+} __wasi_subscription_u_u_t;
+typedef struct __wasi_subscription_u_t {
+ uint8_t tag;
+ __wasi_subscription_u_u_t u;
+} __wasi_subscription_u_t;
+
+_Static_assert(sizeof(__wasi_subscription_u_t) == 40, "witx calculated size");
+_Static_assert(_Alignof(__wasi_subscription_u_t) == 8, "witx calculated align");
+
+/**
+ * Subscription to an event.
+ */
+typedef struct __wasi_subscription_t {
+ /**
+ * User-provided value that is attached to the subscription in the
+ * implementation and returned through `event::userdata`.
+ */
+ __wasi_userdata_t userdata;
+
+ /**
+ * The type of the event to which to subscribe, and its contents
+ */
+ __wasi_subscription_u_t u;
+
+} __wasi_subscription_t;
+
+_Static_assert(sizeof(__wasi_subscription_t) == 48, "witx calculated size");
+_Static_assert(_Alignof(__wasi_subscription_t) == 8, "witx calculated align");
+_Static_assert(offsetof(__wasi_subscription_t, userdata) == 0, "witx calculated offset");
+_Static_assert(offsetof(__wasi_subscription_t, u) == 8, "witx calculated offset");
+
+/**
+ * Exit code generated by a process when exiting.
+ */
+typedef uint32_t __wasi_exitcode_t;
+
+_Static_assert(sizeof(__wasi_exitcode_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_exitcode_t) == 4, "witx calculated align");
+
+/**
+ * Flags provided to `sock_recv`.
+ */
+typedef uint16_t __wasi_riflags_t;
+
+/**
+ * Returns the message without removing it from the socket's receive queue.
+ */
+#define __WASI_RIFLAGS_RECV_PEEK ((__wasi_riflags_t)(1 << 0))
+
+/**
+ * On byte-stream sockets, block until the full amount of data can be returned.
+ */
+#define __WASI_RIFLAGS_RECV_WAITALL ((__wasi_riflags_t)(1 << 1))
+
+/**
+ * Flags returned by `sock_recv`.
+ */
+typedef uint16_t __wasi_roflags_t;
+
+/**
+ * Returned by `sock_recv`: Message data has been truncated.
+ */
+#define __WASI_ROFLAGS_RECV_DATA_TRUNCATED ((__wasi_roflags_t)(1 << 0))
+
+/**
+ * Flags provided to `sock_send`. As there are currently no flags
+ * defined, it must be set to zero.
+ */
+typedef uint16_t __wasi_siflags_t;
+
+_Static_assert(sizeof(__wasi_siflags_t) == 2, "witx calculated size");
+_Static_assert(_Alignof(__wasi_siflags_t) == 2, "witx calculated align");
+
+/**
+ * Which channels on a socket to shut down.
+ */
+typedef uint8_t __wasi_sdflags_t;
+
+/**
+ * Disables further receive operations.
+ */
+#define __WASI_SDFLAGS_RD ((__wasi_sdflags_t)(1 << 0))
+
+/**
+ * Disables further send operations.
+ */
+#define __WASI_SDFLAGS_WR ((__wasi_sdflags_t)(1 << 1))
+
+/**
+ * Identifiers for preopened capabilities.
+ */
+typedef uint8_t __wasi_preopentype_t;
+
+/**
+ * A pre-opened directory.
+ */
+#define __WASI_PREOPENTYPE_DIR (UINT8_C(0))
+
+_Static_assert(sizeof(__wasi_preopentype_t) == 1, "witx calculated size");
+_Static_assert(_Alignof(__wasi_preopentype_t) == 1, "witx calculated align");
+
+/**
+ * The contents of a $prestat when type is `preopentype::dir`.
+ */
+typedef struct __wasi_prestat_dir_t {
+ /**
+ * The length of the directory name for use with `fd_prestat_dir_name`.
+ */
+ __wasi_size_t pr_name_len;
+
+} __wasi_prestat_dir_t;
+
+_Static_assert(sizeof(__wasi_prestat_dir_t) == 4, "witx calculated size");
+_Static_assert(_Alignof(__wasi_prestat_dir_t) == 4, "witx calculated align");
+_Static_assert(offsetof(__wasi_prestat_dir_t, pr_name_len) == 0, "witx calculated offset");
+
+/**
+ * Information about a pre-opened capability.
+ */
+typedef union __wasi_prestat_u_t {
+ __wasi_prestat_dir_t dir;
+} __wasi_prestat_u_t;
+typedef struct __wasi_prestat_t {
+ uint8_t tag;
+ __wasi_prestat_u_t u;
+} __wasi_prestat_t;
+
+_Static_assert(sizeof(__wasi_prestat_t) == 8, "witx calculated size");
+_Static_assert(_Alignof(__wasi_prestat_t) == 4, "witx calculated align");
+
+/**
+ * @defgroup wasi_snapshot_preview1
+ * @{
+ */
+
+/**
+ * Read command-line argument data.
+ * The size of the array should match that returned by `args_sizes_get`.
+ * Each argument is expected to be `\0` terminated.
+ */
+__wasi_errno_t __wasi_args_get(
+ uint8_t * * argv,
+ uint8_t * argv_buf
+) __attribute__((__warn_unused_result__));
+/**
+ * Return command-line argument data sizes.
+ * @return
+ * Returns the number of arguments and the size of the argument string
+ * data, or an error.
+ */
+__wasi_errno_t __wasi_args_sizes_get(
+ __wasi_size_t *retptr0,
+ __wasi_size_t *retptr1
+) __attribute__((__warn_unused_result__));
+/**
+ * Read environment variable data.
+ * The sizes of the buffers should match that returned by `environ_sizes_get`.
+ * Key/value pairs are expected to be joined with `=`s, and terminated with `\0`s.
+ */
+__wasi_errno_t __wasi_environ_get(
+ uint8_t * * environ,
+ uint8_t * environ_buf
+) __attribute__((__warn_unused_result__));
+/**
+ * Return environment variable data sizes.
+ * @return
+ * Returns the number of environment variable arguments and the size of the
+ * environment variable data.
+ */
+__wasi_errno_t __wasi_environ_sizes_get(
+ __wasi_size_t *retptr0,
+ __wasi_size_t *retptr1
+) __attribute__((__warn_unused_result__));
+/**
+ * Return the resolution of a clock.
+ * Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks,
+ * return `errno::inval`.
+ * Note: This is similar to `clock_getres` in POSIX.
+ * @return
+ * The resolution of the clock, or an error if one happened.
+ */
+__wasi_errno_t __wasi_clock_res_get(
+ /**
+ * The clock for which to return the resolution.
+ */
+ __wasi_clockid_t id,
+ __wasi_timestamp_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Return the time value of a clock.
+ * Note: This is similar to `clock_gettime` in POSIX.
+ * @return
+ * The time value of the clock.
+ */
+__wasi_errno_t __wasi_clock_time_get(
+ /**
+ * The clock for which to return the time.
+ */
+ __wasi_clockid_t id,
+ /**
+ * The maximum lag (exclusive) that the returned time value may have, compared to its actual value.
+ */
+ __wasi_timestamp_t precision,
+ __wasi_timestamp_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Provide file advisory information on a file descriptor.
+ * Note: This is similar to `posix_fadvise` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_advise(
+ __wasi_fd_t fd,
+ /**
+ * The offset within the file to which the advisory applies.
+ */
+ __wasi_filesize_t offset,
+ /**
+ * The length of the region to which the advisory applies.
+ */
+ __wasi_filesize_t len,
+ /**
+ * The advice.
+ */
+ __wasi_advice_t advice
+) __attribute__((__warn_unused_result__));
+/**
+ * Force the allocation of space in a file.
+ * Note: This is similar to `posix_fallocate` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_allocate(
+ __wasi_fd_t fd,
+ /**
+ * The offset at which to start the allocation.
+ */
+ __wasi_filesize_t offset,
+ /**
+ * The length of the area that is allocated.
+ */
+ __wasi_filesize_t len
+) __attribute__((__warn_unused_result__));
+/**
+ * Close a file descriptor.
+ * Note: This is similar to `close` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_close(
+ __wasi_fd_t fd
+) __attribute__((__warn_unused_result__));
+/**
+ * Synchronize the data of a file to disk.
+ * Note: This is similar to `fdatasync` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_datasync(
+ __wasi_fd_t fd
+) __attribute__((__warn_unused_result__));
+/**
+ * Get the attributes of a file descriptor.
+ * Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
+ * @return
+ * The buffer where the file descriptor's attributes are stored.
+ */
+__wasi_errno_t __wasi_fd_fdstat_get(
+ __wasi_fd_t fd,
+ __wasi_fdstat_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Adjust the flags associated with a file descriptor.
+ * Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_fdstat_set_flags(
+ __wasi_fd_t fd,
+ /**
+ * The desired values of the file descriptor flags.
+ */
+ __wasi_fdflags_t flags
+) __attribute__((__warn_unused_result__));
+/**
+ * Adjust the rights associated with a file descriptor.
+ * This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights
+ */
+__wasi_errno_t __wasi_fd_fdstat_set_rights(
+ __wasi_fd_t fd,
+ /**
+ * The desired rights of the file descriptor.
+ */
+ __wasi_rights_t fs_rights_base,
+ __wasi_rights_t fs_rights_inheriting
+) __attribute__((__warn_unused_result__));
+/**
+ * Return the attributes of an open file.
+ * @return
+ * The buffer where the file's attributes are stored.
+ */
+__wasi_errno_t __wasi_fd_filestat_get(
+ __wasi_fd_t fd,
+ __wasi_filestat_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
+ * Note: This is similar to `ftruncate` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_filestat_set_size(
+ __wasi_fd_t fd,
+ /**
+ * The desired file size.
+ */
+ __wasi_filesize_t size
+) __attribute__((__warn_unused_result__));
+/**
+ * Adjust the timestamps of an open file or directory.
+ * Note: This is similar to `futimens` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_filestat_set_times(
+ __wasi_fd_t fd,
+ /**
+ * The desired values of the data access timestamp.
+ */
+ __wasi_timestamp_t atim,
+ /**
+ * The desired values of the data modification timestamp.
+ */
+ __wasi_timestamp_t mtim,
+ /**
+ * A bitmask indicating which timestamps to adjust.
+ */
+ __wasi_fstflags_t fst_flags
+) __attribute__((__warn_unused_result__));
+/**
+ * Read from a file descriptor, without using and updating the file descriptor's offset.
+ * Note: This is similar to `preadv` in POSIX.
+ * @return
+ * The number of bytes read.
+ */
+__wasi_errno_t __wasi_fd_pread(
+ __wasi_fd_t fd,
+ /**
+ * List of scatter/gather vectors in which to store data.
+ */
+ const __wasi_iovec_t *iovs,
+ /**
+ * The length of the array pointed to by `iovs`.
+ */
+ size_t iovs_len,
+ /**
+ * The offset within the file at which to read.
+ */
+ __wasi_filesize_t offset,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Return a description of the given preopened file descriptor.
+ * @return
+ * The buffer where the description is stored.
+ */
+__wasi_errno_t __wasi_fd_prestat_get(
+ __wasi_fd_t fd,
+ __wasi_prestat_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Return a description of the given preopened file descriptor.
+ */
+__wasi_errno_t __wasi_fd_prestat_dir_name(
+ __wasi_fd_t fd,
+ /**
+ * A buffer into which to write the preopened directory name.
+ */
+ uint8_t * path,
+ __wasi_size_t path_len
+) __attribute__((__warn_unused_result__));
+/**
+ * Write to a file descriptor, without using and updating the file descriptor's offset.
+ * Note: This is similar to `pwritev` in POSIX.
+ * @return
+ * The number of bytes written.
+ */
+__wasi_errno_t __wasi_fd_pwrite(
+ __wasi_fd_t fd,
+ /**
+ * List of scatter/gather vectors from which to retrieve data.
+ */
+ const __wasi_ciovec_t *iovs,
+ /**
+ * The length of the array pointed to by `iovs`.
+ */
+ size_t iovs_len,
+ /**
+ * The offset within the file at which to write.
+ */
+ __wasi_filesize_t offset,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Read from a file descriptor.
+ * Note: This is similar to `readv` in POSIX.
+ * @return
+ * The number of bytes read.
+ */
+__wasi_errno_t __wasi_fd_read(
+ __wasi_fd_t fd,
+ /**
+ * List of scatter/gather vectors to which to store data.
+ */
+ const __wasi_iovec_t *iovs,
+ /**
+ * The length of the array pointed to by `iovs`.
+ */
+ size_t iovs_len,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Read directory entries from a directory.
+ * When successful, the contents of the output buffer consist of a sequence of
+ * directory entries. Each directory entry consists of a `dirent` object,
+ * followed by `dirent::d_namlen` bytes holding the name of the directory
+ * entry.
+ * This function fills the output buffer as much as possible, potentially
+ * truncating the last directory entry. This allows the caller to grow its
+ * read buffer size in case it's too small to fit a single large directory
+ * entry, or skip the oversized directory entry.
+ * @return
+ * The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached.
+ */
+__wasi_errno_t __wasi_fd_readdir(
+ __wasi_fd_t fd,
+ /**
+ * The buffer where directory entries are stored
+ */
+ uint8_t * buf,
+ __wasi_size_t buf_len,
+ /**
+ * The location within the directory to start reading
+ */
+ __wasi_dircookie_t cookie,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Atomically replace a file descriptor by renumbering another file descriptor.
+ * Due to the strong focus on thread safety, this environment does not provide
+ * a mechanism to duplicate or renumber a file descriptor to an arbitrary
+ * number, like `dup2()`. This would be prone to race conditions, as an actual
+ * file descriptor with the same number could be allocated by a different
+ * thread at the same time.
+ * This function provides a way to atomically renumber file descriptors, which
+ * would disappear if `dup2()` were to be removed entirely.
+ */
+__wasi_errno_t __wasi_fd_renumber(
+ __wasi_fd_t fd,
+ /**
+ * The file descriptor to overwrite.
+ */
+ __wasi_fd_t to
+) __attribute__((__warn_unused_result__));
+/**
+ * Move the offset of a file descriptor.
+ * Note: This is similar to `lseek` in POSIX.
+ * @return
+ * The new offset of the file descriptor, relative to the start of the file.
+ */
+__wasi_errno_t __wasi_fd_seek(
+ __wasi_fd_t fd,
+ /**
+ * The number of bytes to move.
+ */
+ __wasi_filedelta_t offset,
+ /**
+ * The base from which the offset is relative.
+ */
+ __wasi_whence_t whence,
+ __wasi_filesize_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Synchronize the data and metadata of a file to disk.
+ * Note: This is similar to `fsync` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_sync(
+ __wasi_fd_t fd
+) __attribute__((__warn_unused_result__));
+/**
+ * Return the current offset of a file descriptor.
+ * Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
+ * @return
+ * The current offset of the file descriptor, relative to the start of the file.
+ */
+__wasi_errno_t __wasi_fd_tell(
+ __wasi_fd_t fd,
+ __wasi_filesize_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Write to a file descriptor.
+ * Note: This is similar to `writev` in POSIX.
+ */
+__wasi_errno_t __wasi_fd_write(
+ __wasi_fd_t fd,
+ /**
+ * List of scatter/gather vectors from which to retrieve data.
+ */
+ const __wasi_ciovec_t *iovs,
+ /**
+ * The length of the array pointed to by `iovs`.
+ */
+ size_t iovs_len,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Create a directory.
+ * Note: This is similar to `mkdirat` in POSIX.
+ */
+__wasi_errno_t __wasi_path_create_directory(
+ __wasi_fd_t fd,
+ /**
+ * The path at which to create the directory.
+ */
+ const char *path
+) __attribute__((__warn_unused_result__));
+/**
+ * Return the attributes of a file or directory.
+ * Note: This is similar to `stat` in POSIX.
+ * @return
+ * The buffer where the file's attributes are stored.
+ */
+__wasi_errno_t __wasi_path_filestat_get(
+ __wasi_fd_t fd,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ __wasi_lookupflags_t flags,
+ /**
+ * The path of the file or directory to inspect.
+ */
+ const char *path,
+ __wasi_filestat_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Adjust the timestamps of a file or directory.
+ * Note: This is similar to `utimensat` in POSIX.
+ */
+__wasi_errno_t __wasi_path_filestat_set_times(
+ __wasi_fd_t fd,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ __wasi_lookupflags_t flags,
+ /**
+ * The path of the file or directory to operate on.
+ */
+ const char *path,
+ /**
+ * The desired values of the data access timestamp.
+ */
+ __wasi_timestamp_t atim,
+ /**
+ * The desired values of the data modification timestamp.
+ */
+ __wasi_timestamp_t mtim,
+ /**
+ * A bitmask indicating which timestamps to adjust.
+ */
+ __wasi_fstflags_t fst_flags
+) __attribute__((__warn_unused_result__));
+/**
+ * Create a hard link.
+ * Note: This is similar to `linkat` in POSIX.
+ */
+__wasi_errno_t __wasi_path_link(
+ __wasi_fd_t old_fd,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ __wasi_lookupflags_t old_flags,
+ /**
+ * The source path from which to link.
+ */
+ const char *old_path,
+ /**
+ * The working directory at which the resolution of the new path starts.
+ */
+ __wasi_fd_t new_fd,
+ /**
+ * The destination path at which to create the hard link.
+ */
+ const char *new_path
+) __attribute__((__warn_unused_result__));
+/**
+ * Open a file or directory.
+ * The returned file descriptor is not guaranteed to be the lowest-numbered
+ * file descriptor not currently open; it is randomized to prevent
+ * applications from depending on making assumptions about indexes, since this
+ * is error-prone in multi-threaded contexts. The returned file descriptor is
+ * guaranteed to be less than 2**31.
+ * Note: This is similar to `openat` in POSIX.
+ * @return
+ * The file descriptor of the file that has been opened.
+ */
+__wasi_errno_t __wasi_path_open(
+ __wasi_fd_t fd,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ __wasi_lookupflags_t dirflags,
+ /**
+ * The relative path of the file or directory to open, relative to the
+ * `path_open::fd` directory.
+ */
+ const char *path,
+ /**
+ * The method by which to open the file.
+ */
+ __wasi_oflags_t oflags,
+ /**
+ * The initial rights of the newly created file descriptor. The
+ * implementation is allowed to return a file descriptor with fewer rights
+ * than specified, if and only if those rights do not apply to the type of
+ * file being opened.
+ * The *base* rights are rights that will apply to operations using the file
+ * descriptor itself, while the *inheriting* rights are rights that apply to
+ * file descriptors derived from it.
+ */
+ __wasi_rights_t fs_rights_base,
+ __wasi_rights_t fs_rights_inheriting,
+ __wasi_fdflags_t fdflags,
+ __wasi_fd_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Read the contents of a symbolic link.
+ * Note: This is similar to `readlinkat` in POSIX.
+ * @return
+ * The number of bytes placed in the buffer.
+ */
+__wasi_errno_t __wasi_path_readlink(
+ __wasi_fd_t fd,
+ /**
+ * The path of the symbolic link from which to read.
+ */
+ const char *path,
+ /**
+ * The buffer to which to write the contents of the symbolic link.
+ */
+ uint8_t * buf,
+ __wasi_size_t buf_len,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Remove a directory.
+ * Return `errno::notempty` if the directory is not empty.
+ * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+ */
+__wasi_errno_t __wasi_path_remove_directory(
+ __wasi_fd_t fd,
+ /**
+ * The path to a directory to remove.
+ */
+ const char *path
+) __attribute__((__warn_unused_result__));
+/**
+ * Rename a file or directory.
+ * Note: This is similar to `renameat` in POSIX.
+ */
+__wasi_errno_t __wasi_path_rename(
+ __wasi_fd_t fd,
+ /**
+ * The source path of the file or directory to rename.
+ */
+ const char *old_path,
+ /**
+ * The working directory at which the resolution of the new path starts.
+ */
+ __wasi_fd_t new_fd,
+ /**
+ * The destination path to which to rename the file or directory.
+ */
+ const char *new_path
+) __attribute__((__warn_unused_result__));
+/**
+ * Create a symbolic link.
+ * Note: This is similar to `symlinkat` in POSIX.
+ */
+__wasi_errno_t __wasi_path_symlink(
+ /**
+ * The contents of the symbolic link.
+ */
+ const char *old_path,
+ __wasi_fd_t fd,
+ /**
+ * The destination path at which to create the symbolic link.
+ */
+ const char *new_path
+) __attribute__((__warn_unused_result__));
+/**
+ * Unlink a file.
+ * Return `errno::isdir` if the path refers to a directory.
+ * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+ */
+__wasi_errno_t __wasi_path_unlink_file(
+ __wasi_fd_t fd,
+ /**
+ * The path to a file to unlink.
+ */
+ const char *path
+) __attribute__((__warn_unused_result__));
+/**
+ * Concurrently poll for the occurrence of a set of events.
+ * @return
+ * The number of events stored.
+ */
+__wasi_errno_t __wasi_poll_oneoff(
+ /**
+ * The events to which to subscribe.
+ */
+ const __wasi_subscription_t * in,
+ /**
+ * The events that have occurred.
+ */
+ __wasi_event_t * out,
+ /**
+ * Both the number of subscriptions and events.
+ */
+ __wasi_size_t nsubscriptions,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Terminate the process normally. An exit code of 0 indicates successful
+ * termination of the program. The meanings of other values is dependent on
+ * the environment.
+ */
+_Noreturn void __wasi_proc_exit(
+ /**
+ * The exit code returned by the process.
+ */
+ __wasi_exitcode_t rval
+);
+/**
+ * Temporarily yield execution of the calling thread.
+ * Note: This is similar to `sched_yield` in POSIX.
+ */
+__wasi_errno_t __wasi_sched_yield(
+ void
+) __attribute__((__warn_unused_result__));
+/**
+ * Write high-quality random data into a buffer.
+ * This function blocks when the implementation is unable to immediately
+ * provide sufficient high-quality random data.
+ * This function may execute slowly, so when large mounts of random data are
+ * required, it's advisable to use this function to seed a pseudo-random
+ * number generator, rather than to provide the random data directly.
+ */
+__wasi_errno_t __wasi_random_get(
+ /**
+ * The buffer to fill with random data.
+ */
+ uint8_t * buf,
+ __wasi_size_t buf_len
+) __attribute__((__warn_unused_result__));
+/**
+ * Accept a new incoming connection.
+ * Note: This is similar to `accept` in POSIX.
+ * @return
+ * New socket connection
+ */
+__wasi_errno_t __wasi_sock_accept(
+ /**
+ * The listening socket.
+ */
+ __wasi_fd_t fd,
+ /**
+ * The desired values of the file descriptor flags.
+ */
+ __wasi_fdflags_t flags,
+ __wasi_fd_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Receive a message from a socket.
+ * Note: This is similar to `recv` in POSIX, though it also supports reading
+ * the data into multiple buffers in the manner of `readv`.
+ * @return
+ * Number of bytes stored in ri_data and message flags.
+ */
+__wasi_errno_t __wasi_sock_recv(
+ __wasi_fd_t fd,
+ /**
+ * List of scatter/gather vectors to which to store data.
+ */
+ const __wasi_iovec_t *ri_data,
+ /**
+ * The length of the array pointed to by `ri_data`.
+ */
+ size_t ri_data_len,
+ /**
+ * Message flags.
+ */
+ __wasi_riflags_t ri_flags,
+ __wasi_size_t *retptr0,
+ __wasi_roflags_t *retptr1
+) __attribute__((__warn_unused_result__));
+/**
+ * Send a message on a socket.
+ * Note: This is similar to `send` in POSIX, though it also supports writing
+ * the data from multiple buffers in the manner of `writev`.
+ * @return
+ * Number of bytes transmitted.
+ */
+__wasi_errno_t __wasi_sock_send(
+ __wasi_fd_t fd,
+ /**
+ * List of scatter/gather vectors to which to retrieve data
+ */
+ const __wasi_ciovec_t *si_data,
+ /**
+ * The length of the array pointed to by `si_data`.
+ */
+ size_t si_data_len,
+ /**
+ * Message flags.
+ */
+ __wasi_siflags_t si_flags,
+ __wasi_size_t *retptr0
+) __attribute__((__warn_unused_result__));
+/**
+ * Shut down socket send and receive channels.
+ * Note: This is similar to `shutdown` in POSIX.
+ */
+__wasi_errno_t __wasi_sock_shutdown(
+ __wasi_fd_t fd,
+ /**
+ * Which channels on the socket to shut down.
+ */
+ __wasi_sdflags_t how
+) __attribute__((__warn_unused_result__));
+/** @} */
+
+#ifdef _REENTRANT
+/**
+ * Request a new thread to be created by the host.
+ *
+ * The host will create a new instance of the current module sharing its
+ * memory, find an exported entry function--`wasi_thread_start`--, and call the
+ * entry function with `start_arg` in the new thread.
+ *
+ * @see https://github.com/WebAssembly/wasi-threads/#readme
+ */
+__wasi_errno_t __wasi_thread_spawn(
+ /**
+ * A pointer to an opaque struct to be passed to the module's entry
+ * function.
+ */
+ void *start_arg
+) __attribute__((__warn_unused_result__));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/wasi/libc-environ.h b/libc-bottom-half/headers/public/wasi/libc-environ.h
new file mode 100644
index 0000000..f84ba8e
--- /dev/null
+++ b/libc-bottom-half/headers/public/wasi/libc-environ.h
@@ -0,0 +1,36 @@
+#ifndef __wasi_libc_environ_h
+#define __wasi_libc_environ_h
+
+/// This header file is a WASI-libc-specific interface, and is not needed by
+/// most programs. Most programs should just use the standard `getenv` and
+/// related APIs, which take care of all of the details automatically.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/// Initialize the global environment variable state. Only needs to be
+/// called once; most users should call `__wasilibc_ensure_environ` instead.
+void __wasilibc_initialize_environ(void);
+
+/// If `__wasilibc_initialize_environ` has not yet been called, call it.
+void __wasilibc_ensure_environ(void);
+
+/// De-initialize the global environment variable state, so that subsequent
+/// calls to `__wasilibc_ensure_environ` call `__wasilibc_initialize_environ`.
+void __wasilibc_deinitialize_environ(void);
+
+/// Call `__wasilibc_initialize_environ` only if `environ` and `_environ` are
+/// referenced in the program.
+void __wasilibc_maybe_reinitialize_environ_eagerly(void);
+
+/// Return the value of the `environ` variable. Using `environ` directly
+/// requires eager initialization of the environment variables. Using this
+/// function instead of `environ` allows initialization to happen lazily.
+char **__wasilibc_get_environ(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/wasi/libc-find-relpath.h b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h
new file mode 100644
index 0000000..32dbb03
--- /dev/null
+++ b/libc-bottom-half/headers/public/wasi/libc-find-relpath.h
@@ -0,0 +1,79 @@
+#ifndef __wasi_libc_find_relpath_h
+#define __wasi_libc_find_relpath_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Look up the given `path`, relative to the cwd, in the preopened directory
+ * map. If a suitable entry is found, then the file descriptor for that entry
+ * is returned. Additionally the absolute path of the directory's file
+ * descriptor is returned in `abs_prefix` and the relative portion that needs
+ * to be opened is stored in `*relative_path`.
+ *
+ * The `relative_path` argument must be a pointer to a buffer valid for
+ * `relative_path_len` bytes, and this may be used as storage for the relative
+ * portion of the path being returned through `*relative_path`.
+ *
+ * See documentation on `__wasilibc_find_abspath` for more info about what the
+ * paths look like.
+ *
+ * Returns -1 on failure. Errno is set to either:
+ *
+ * * ENOMEM - failed to allocate memory for internal routines.
+ * * ENOENT - the `path` could not be found relative to any preopened dir.
+ * * ERANGE - the `relative_path` buffer is too small to hold the relative path.
+ */
+int __wasilibc_find_relpath(const char *path,
+ const char **__restrict__ abs_prefix,
+ char **relative_path,
+ size_t relative_path_len);
+
+/**
+ * Look up the given `path`, which is interpreted as absolute, in the preopened
+ * directory map. If a suitable entry is found, then the file descriptor for
+ * that entry is returned. Additionally the relative portion of the path to
+ * where the fd is opened is returned through `relative_path`, the absolute
+ * prefix which was matched is stored to `abs_prefix`, and `relative_path` may
+ * be an interior pointer to the `abspath` string.
+ *
+ * The `abs_prefix` returned string will not contain a leading `/`. Note that
+ * this may be the empty string. Additionally the returned `relative_path` will
+ * not contain a leading `/`. The `relative_path` return will not return an
+ * empty string, it will return `"."` instead if it would otherwise do so.
+ *
+ * Returns -1 on failure. Errno is set to either:
+ *
+ * * ENOMEM - failed to allocate memory for internal routines.
+ * * ENOENT - the `path` could not be found relative to any preopened dir.
+ */
+int __wasilibc_find_abspath(const char *abspath,
+ const char **__restrict__ abs_prefix,
+ const char **__restrict__ relative_path);
+
+/**
+ * Same as `__wasilibc_find_relpath`, except that this function will interpret
+ * `relative` as a malloc'd buffer that will be `realloc`'d to the appropriate
+ * size to contain the relative path.
+ *
+ * Note that this is a weak symbol and if it's not defined you can use
+ * `__wasilibc_find_relpath`. The weak-nature of this symbols means that if it's
+ * not otherwise included in the compilation then `chdir` wasn't used an there's
+ * no need for this symbol.
+ *
+ * See documentation on `__wasilibc_find_relpath` for more information.
+ */
+int __wasilibc_find_relpath_alloc(
+ const char *path,
+ const char **abs,
+ char **relative,
+ size_t *relative_len,
+ int can_realloc
+) __attribute__((__weak__));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/wasi/libc-nocwd.h b/libc-bottom-half/headers/public/wasi/libc-nocwd.h
new file mode 100644
index 0000000..a988503
--- /dev/null
+++ b/libc-bottom-half/headers/public/wasi/libc-nocwd.h
@@ -0,0 +1,58 @@
+#ifndef __wasi_libc_nocwd_h
+#define __wasi_libc_nocwd_h
+
+/*
+ * In order to support AT_FDCWD, we need to wrap the *at functions to handle
+ * it by calling back into the non-at versions which perform libpreopen
+ * queries. These __wasilibc_nocwd_* forms are the underlying calls which
+ * assume AT_FDCWD has already been resolved.
+ */
+
+#define __need_size_t
+#include <stddef.h>
+#include <__typedef_ssize_t.h>
+#include <__typedef_mode_t.h>
+#include <__typedef_DIR.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct timespec;
+struct stat;
+struct dirent;
+
+int __wasilibc_nocwd___wasilibc_unlinkat(int, const char *)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd___wasilibc_rmdirat(int, const char *)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_linkat(int, const char *, int, const char *, int)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_symlinkat(const char *, int, const char *)
+ __attribute__((__warn_unused_result__));
+ssize_t __wasilibc_nocwd_readlinkat(int, const char *__restrict, char *__restrict, size_t)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_faccessat(int, const char *, int, int)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_renameat(int, const char *, int, const char *)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_openat_nomode(int, const char *, int)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_fstatat(int, const char *__restrict, struct stat *__restrict, int)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_mkdirat_nomode(int, const char *)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_utimensat(int, const char *, const struct timespec [2], int)
+ __attribute__((__warn_unused_result__));
+DIR *__wasilibc_nocwd_opendirat(int, const char *)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_nocwd_scandirat(int, const char *, struct dirent ***,
+ int (*)(const struct dirent *),
+ int (*)(const struct dirent **, const struct dirent **))
+ __attribute__((__warn_unused_result__));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/wasi/libc.h b/libc-bottom-half/headers/public/wasi/libc.h
new file mode 100644
index 0000000..b50518b
--- /dev/null
+++ b/libc-bottom-half/headers/public/wasi/libc.h
@@ -0,0 +1,65 @@
+#ifndef __wasi_libc_h
+#define __wasi_libc_h
+
+#include <__typedef_off_t.h>
+#include <__struct_timespec.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct stat;
+struct timespec;
+
+/// Register the given pre-opened file descriptor under the given path.
+///
+/// This function does not take ownership of `prefix` (it makes its own copy).
+int __wasilibc_register_preopened_fd(int fd, const char *prefix);
+
+/// Renumber `fd` to `newfd`; similar to `dup2` but does a move rather than a
+/// copy.
+int __wasilibc_fd_renumber(int fd, int newfd)
+ __attribute__((__warn_unused_result__));
+
+/// Like `unlinkat`, but without depending on `__wasi_path_remove_directory`.
+int __wasilibc_unlinkat(int fd, const char *path)
+ __attribute__((__warn_unused_result__));
+
+/// An `*at` version of rmdir.
+int __wasilibc_rmdirat(int fd, const char *path)
+ __attribute__((__warn_unused_result__));
+
+/// Like `open`, but without the varargs in the signature.
+int __wasilibc_open_nomode(const char *path, int oflag);
+
+/// Like `openat`, but without the varargs in the signature.
+int __wasilibc_openat_nomode(int fd, const char *path, int oflag);
+
+/// Return the current file offset. Like `lseek(fd, 0, SEEK_CUR)`, but without
+/// depending on `lseek`.
+off_t __wasilibc_tell(int fd)
+ __attribute__((__warn_unused_result__));
+
+/* Non-`at` forms of various `*at` functions. */
+int __wasilibc_access(const char *pathname, int mode, int flags)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_stat(const char *__restrict pathname, struct stat *__restrict statbuf, int flags)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_utimens(const char *pathname, const struct timespec times[2], int flags)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_link(const char *oldpath, const char *newpath, int flags)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_link_oldat(int olddirfd, const char *oldpath, const char *newpath, int flags)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_link_newat(const char *oldpath, int newdirfd, const char *newpath, int flags)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_rename_oldat(int olddirfd, const char *oldpath, const char *newpath)
+ __attribute__((__warn_unused_result__));
+int __wasilibc_rename_newat(const char *oldpath, int newdirfd, const char *newpath)
+ __attribute__((__warn_unused_result__));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libc-bottom-half/headers/public/wchar.h b/libc-bottom-half/headers/public/wchar.h
new file mode 100644
index 0000000..f43d72f
--- /dev/null
+++ b/libc-bottom-half/headers/public/wchar.h
@@ -0,0 +1,9 @@
+#ifndef __wasilibc_wchar_h
+#define __wasilibc_wchar_h
+
+#define __need_size_t
+#define __need_wchar_t
+#define __need_NULL
+#include <stddef.h>
+
+#endif
diff --git a/libc-bottom-half/mman/mman.c b/libc-bottom-half/mman/mman.c
new file mode 100644
index 0000000..f02b136
--- /dev/null
+++ b/libc-bottom-half/mman/mman.c
@@ -0,0 +1,124 @@
+// Userspace emulation of mmap and munmap. Restrictions apply.
+//
+// This is meant to be complete enough to be compatible with code that uses
+// mmap for simple file I/O. It just allocates memory with malloc and reads
+// and writes data with pread and pwrite.
+
+#define _WASI_EMULATED_MMAN
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+struct map {
+ int prot;
+ int flags;
+ off_t offset;
+ size_t length;
+};
+
+void *mmap(void *addr, size_t length, int prot, int flags,
+ int fd, off_t offset) {
+ // Check for unsupported flags.
+ if ((flags & (MAP_PRIVATE | MAP_SHARED)) == 0 ||
+ (flags & MAP_FIXED) != 0 ||
+#ifdef MAP_SHARED_VALIDATE
+ (flags & MAP_SHARED_VALIDATE) == MAP_SHARED_VALIDATE ||
+#endif
+#ifdef MAP_NORESERVE
+ (flags & MAP_NORESERVE) != 0 ||
+#endif
+#ifdef MAP_GROWSDOWN
+ (flags & MAP_GROWSDOWN) != 0 ||
+#endif
+#ifdef MAP_HUGETLB
+ (flags & MAP_HUGETLB) != 0 ||
+#endif
+#ifdef MAP_FIXED_NOREPLACE
+ (flags & MAP_FIXED_NOREPLACE) != 0 ||
+#endif
+ 0)
+ {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ // Check for unsupported protection requests.
+ if (prot == PROT_NONE ||
+#ifdef PROT_EXEC
+ (prot & PROT_EXEC) != 0 ||
+#endif
+ 0)
+ {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ // To be consistent with POSIX.
+ if (length == 0) {
+ errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ // Check for integer overflow.
+ size_t buf_len = 0;
+ if (__builtin_add_overflow(length, sizeof(struct map), &buf_len)) {
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
+
+ // Allocate the memory.
+ struct map *map = malloc(buf_len);
+ if (!map) {
+ errno = ENOMEM;
+ return MAP_FAILED;
+ }
+
+ // Initialize the header.
+ map->prot = prot;
+ map->flags = flags;
+ map->offset = offset;
+ map->length = length;
+
+ // Initialize the main memory buffer, either with the contents of a file,
+ // or with zeros.
+ addr = map + 1;
+ if ((flags & MAP_ANON) == 0) {
+ char *body = (char *)addr;
+ while (length > 0) {
+ const ssize_t nread = pread(fd, body, length, offset);
+ if (nread < 0) {
+ if (errno == EINTR)
+ continue;
+ return MAP_FAILED;
+ }
+ if (nread == 0)
+ break;
+ length -= (size_t)nread;
+ offset += (size_t)nread;
+ body += (size_t)nread;
+ }
+ } else {
+ memset(addr, 0, length);
+ }
+
+ return addr;
+}
+
+int munmap(void *addr, size_t length) {
+ struct map *map = (struct map *)addr - 1;
+
+ // We don't support partial munmapping.
+ if (map->length != length) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Release the memory.
+ free(map);
+
+ // Success!
+ return 0;
+}
diff --git a/libc-bottom-half/signal/signal.c b/libc-bottom-half/signal/signal.c
new file mode 100644
index 0000000..290d6b2
--- /dev/null
+++ b/libc-bottom-half/signal/signal.c
@@ -0,0 +1,142 @@
+// Userspace emulation of `raise` and `signal`.
+//
+// WebAssembly doesn't support asynchronous signal delivery, so we can't
+// support it in WASI libc. But we can make things like `raise` work.
+
+#define _WASI_EMULATED_SIGNAL
+#define _ALL_SOURCE
+#define _GNU_SOURCE
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+void __SIG_IGN(int sig) {
+ // do nothing
+}
+
+_Noreturn
+void __SIG_ERR(int sig) {
+ __builtin_trap();
+}
+
+_Noreturn
+static void core_handler(int sig) {
+ fprintf(stderr, "Program received fatal signal: %s\n", strsignal(sig));
+ abort();
+}
+
+_Noreturn
+static void terminate_handler(int sig) {
+ fprintf(stderr, "Program recieved termination signal: %s\n", strsignal(sig));
+ abort();
+}
+
+_Noreturn
+static void stop_handler(int sig) {
+ fprintf(stderr, "Program recieved stop signal: %s\n", strsignal(sig));
+ abort();
+}
+
+static void continue_handler(int sig) {
+ // do nothing
+}
+
+static const sighandler_t default_handlers[_NSIG] = {
+ // Default behavior: "core".
+ [SIGABRT] = core_handler,
+ [SIGBUS] = core_handler,
+ [SIGFPE] = core_handler,
+ [SIGILL] = core_handler,
+#if SIGIOT != SIGABRT
+ [SIGIOT] = core_handler,
+#endif
+ [SIGQUIT] = core_handler,
+ [SIGSEGV] = core_handler,
+ [SIGSYS] = core_handler,
+ [SIGTRAP] = core_handler,
+ [SIGXCPU] = core_handler,
+ [SIGXFSZ] = core_handler,
+#if defined(SIGUNUSED) && SIGUNUSED != SIGSYS
+ [SIGUNUSED] = core_handler,
+#endif
+
+ // Default behavior: ignore.
+ [SIGCHLD] = SIG_IGN,
+#if defined(SIGCLD) && SIGCLD != SIGCHLD
+ [SIGCLD] = SIG_IGN,
+#endif
+ [SIGURG] = SIG_IGN,
+ [SIGWINCH] = SIG_IGN,
+
+ // Default behavior: "continue".
+ [SIGCONT] = continue_handler,
+
+ // Default behavior: "stop".
+ [SIGSTOP] = stop_handler,
+ [SIGTSTP] = stop_handler,
+ [SIGTTIN] = stop_handler,
+ [SIGTTOU] = stop_handler,
+
+ // Default behavior: "terminate".
+ [SIGHUP] = terminate_handler,
+ [SIGINT] = terminate_handler,
+ [SIGKILL] = terminate_handler,
+ [SIGUSR1] = terminate_handler,
+ [SIGUSR2] = terminate_handler,
+ [SIGPIPE] = terminate_handler,
+ [SIGALRM] = terminate_handler,
+ [SIGTERM] = terminate_handler,
+ [SIGSTKFLT] = terminate_handler,
+ [SIGVTALRM] = terminate_handler,
+ [SIGPROF] = terminate_handler,
+ [SIGIO] = terminate_handler,
+#if SIGPOLL != SIGIO
+ [SIGPOLL] = terminate_handler,
+#endif
+ [SIGPWR] = terminate_handler,
+};
+
+static sighandler_t handlers[_NSIG];
+
+int raise(int sig) {
+ if (sig < 0 || sig >= _NSIG) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ sighandler_t func = handlers[sig];
+
+ if (func == NULL) {
+ default_handlers[sig](sig);
+ } else {
+ func(sig);
+ }
+
+ return 0;
+}
+
+void (*signal(int sig, void (*func)(int)))(int) {
+ assert(SIG_DFL == NULL);
+
+ if (sig < 0 || sig >= _NSIG) {
+ errno = EINVAL;
+ return SIG_ERR;
+ }
+
+ if (sig == SIGKILL || sig == SIGSTOP) {
+ errno = EINVAL;
+ return SIG_ERR;
+ }
+
+ sighandler_t old = handlers[sig];
+
+ handlers[sig] = func;
+
+ return old;
+}
+
+extern __typeof(signal) bsd_signal __attribute__((weak, alias("signal")));
+extern __typeof(signal) __sysv_signal __attribute__((weak, alias("signal")));
diff --git a/libc-bottom-half/sources/__errno_location.c b/libc-bottom-half/sources/__errno_location.c
new file mode 100644
index 0000000..5e6ef5e
--- /dev/null
+++ b/libc-bottom-half/sources/__errno_location.c
@@ -0,0 +1,5 @@
+#include <errno.h>
+
+int *__errno_location(void) {
+ return &errno;
+}
diff --git a/libc-bottom-half/sources/__main_void.c b/libc-bottom-half/sources/__main_void.c
new file mode 100644
index 0000000..cba22ef
--- /dev/null
+++ b/libc-bottom-half/sources/__main_void.c
@@ -0,0 +1,54 @@
+#include <wasi/api.h>
+#include <stdlib.h>
+#include <sysexits.h>
+
+// The user's `main` function, expecting arguments.
+int __main_argc_argv(int argc, char *argv[]);
+
+// If the user's `main` function expects arguments, the compiler will rename
+// it to `__main_argc_argv`, and this version will get linked in, which
+// initializes the argument data and calls `__main_argc_argv`.
+__attribute__((__weak__, nodebug))
+int __main_void(void) {
+ __wasi_errno_t err;
+
+ // Get the sizes of the arrays we'll have to create to copy in the args.
+ size_t argv_buf_size;
+ size_t argc;
+ err = __wasi_args_sizes_get(&argc, &argv_buf_size);
+ if (err != __WASI_ERRNO_SUCCESS) {
+ _Exit(EX_OSERR);
+ }
+
+ // Add 1 for the NULL pointer to mark the end, and check for overflow.
+ size_t num_ptrs = argc + 1;
+ if (num_ptrs == 0) {
+ _Exit(EX_SOFTWARE);
+ }
+
+ // Allocate memory for storing the argument chars.
+ char *argv_buf = malloc(argv_buf_size);
+ if (argv_buf == NULL) {
+ _Exit(EX_SOFTWARE);
+ }
+
+ // Allocate memory for the array of pointers. This uses `calloc` both to
+ // handle overflow and to initialize the NULL pointer at the end.
+ char **argv = calloc(num_ptrs, sizeof(char *));
+ if (argv == NULL) {
+ free(argv_buf);
+ _Exit(EX_SOFTWARE);
+ }
+
+ // Fill the argument chars, and the argv array with pointers into those chars.
+ // TODO: Remove the casts on `argv_ptrs` and `argv_buf` once the witx is updated with char8 support.
+ err = __wasi_args_get((uint8_t **)argv, (uint8_t *)argv_buf);
+ if (err != __WASI_ERRNO_SUCCESS) {
+ free(argv_buf);
+ free(argv);
+ _Exit(EX_OSERR);
+ }
+
+ // Call `__main_argc_argv` with the arguments!
+ return __main_argc_argv(argc, argv);
+}
diff --git a/libc-bottom-half/sources/__wasilibc_dt.c b/libc-bottom-half/sources/__wasilibc_dt.c
new file mode 100644
index 0000000..b06460f
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_dt.c
@@ -0,0 +1,34 @@
+#include <__header_dirent.h>
+#include <__mode_t.h>
+
+int __wasilibc_iftodt(int x) {
+ switch (x) {
+ case S_IFDIR: return DT_DIR;
+ case S_IFCHR: return DT_CHR;
+ case S_IFBLK: return DT_BLK;
+ case S_IFREG: return DT_REG;
+ case S_IFIFO: return DT_FIFO;
+ case S_IFLNK: return DT_LNK;
+#ifdef DT_SOCK
+ case S_IFSOCK: return DT_SOCK;
+#endif
+ default: return DT_UNKNOWN;
+ }
+}
+
+int __wasilibc_dttoif(int x) {
+ switch (x) {
+ case DT_DIR: return S_IFDIR;
+ case DT_CHR: return S_IFCHR;
+ case DT_BLK: return S_IFBLK;
+ case DT_REG: return S_IFREG;
+ case DT_FIFO: return S_IFIFO;
+ case DT_LNK: return S_IFLNK;
+#ifdef DT_SOCK
+ case DT_SOCK: return S_IFSOCK;
+#endif
+ case DT_UNKNOWN:
+ default:
+ return S_IFSOCK;
+ }
+}
diff --git a/libc-bottom-half/sources/__wasilibc_environ.c b/libc-bottom-half/sources/__wasilibc_environ.c
new file mode 100644
index 0000000..53d0a55
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_environ.c
@@ -0,0 +1,14 @@
+#include <wasi/libc-environ.h>
+
+extern char **__wasilibc_environ;
+
+// See the comments in libc-environ.h.
+char **__wasilibc_get_environ(void) {
+ // Perform lazy initialization if needed.
+ __wasilibc_ensure_environ();
+
+ // Return `environ`. Use the `__wasilibc_`-prefixed name so that we don't
+ // pull in the `environ` symbol directly, which would lead to eager
+ // initialization being done instead.
+ return __wasilibc_environ;
+}
diff --git a/libc-bottom-half/sources/__wasilibc_fd_renumber.c b/libc-bottom-half/sources/__wasilibc_fd_renumber.c
new file mode 100644
index 0000000..aa9d8dc
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_fd_renumber.c
@@ -0,0 +1,13 @@
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+#include <unistd.h>
+
+int __wasilibc_fd_renumber(int fd, int newfd) {
+ __wasi_errno_t error = __wasi_fd_renumber(fd, newfd);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/sources/__wasilibc_initialize_environ.c b/libc-bottom-half/sources/__wasilibc_initialize_environ.c
new file mode 100644
index 0000000..2d31c5d
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_initialize_environ.c
@@ -0,0 +1,93 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <wasi/libc-environ.h>
+
+/// If the program doesn't use `environ`, it'll get this version of
+/// `__wasilibc_environ`, which isn't initialized with a constructor function.
+/// `getenv` etc. call `__wasilibc_ensure_environ()` before accessing it.
+/// Statically-initialize it to an invalid pointer value so that we can
+/// detect if it's been explicitly initialized (we can't use `NULL` because
+/// `clearenv` sets it to NULL.
+weak char **__wasilibc_environ = (char **)-1;
+
+// See the comments in libc-environ.h.
+void __wasilibc_ensure_environ(void) {
+ if (__wasilibc_environ == (char **)-1) {
+ __wasilibc_initialize_environ();
+ }
+}
+
+/// Avoid dynamic allocation for the case where there are no environment
+/// variables, but we still need a non-NULL pointer to an (empty) array.
+static char *empty_environ[1] = { NULL };
+
+// See the comments in libc-environ.h.
+void __wasilibc_initialize_environ(void) {
+ // Get the sizes of the arrays we'll have to create to copy in the environment.
+ size_t environ_count;
+ size_t environ_buf_size;
+ __wasi_errno_t err = __wasi_environ_sizes_get(&environ_count, &environ_buf_size);
+ if (err != __WASI_ERRNO_SUCCESS) {
+ goto oserr;
+ }
+ if (environ_count == 0) {
+ __wasilibc_environ = empty_environ;
+ return;
+ }
+
+ // Add 1 for the NULL pointer to mark the end, and check for overflow.
+ size_t num_ptrs = environ_count + 1;
+ if (num_ptrs == 0) {
+ goto software;
+ }
+
+ // Allocate memory for storing the environment chars.
+ char *environ_buf = malloc(environ_buf_size);
+ if (environ_buf == NULL) {
+ goto software;
+ }
+
+ // Allocate memory for the array of pointers. This uses `calloc` both to
+ // handle overflow and to initialize the NULL pointer at the end.
+ char **environ_ptrs = calloc(num_ptrs, sizeof(char *));
+ if (environ_ptrs == NULL) {
+ free(environ_buf);
+ goto software;
+ }
+
+ // Fill the environment chars, and the `__wasilibc_environ` array with
+ // pointers into those chars.
+ // TODO: Remove the casts on `environ_ptrs` and `environ_buf` once the witx is updated with char8 support.
+ err = __wasi_environ_get((uint8_t **)environ_ptrs, (uint8_t *)environ_buf);
+ if (err != __WASI_ERRNO_SUCCESS) {
+ free(environ_buf);
+ free(environ_ptrs);
+ goto oserr;
+ }
+
+ __wasilibc_environ = environ_ptrs;
+ return;
+oserr:
+ _Exit(EX_OSERR);
+software:
+ _Exit(EX_SOFTWARE);
+}
+
+// See the comments in libc-environ.h.
+void __wasilibc_deinitialize_environ(void) {
+ if (__wasilibc_environ != (char **)-1) {
+ // Let libc-top-half clear the old environment-variable strings.
+ clearenv();
+ // Set the pointer to the special init value.
+ __wasilibc_environ = (char **)-1;
+ }
+}
+
+// See the comments in libc-environ.h.
+weak void __wasilibc_maybe_reinitialize_environ_eagerly(void) {
+ // This version does nothing. It may be overridden by a version which does
+ // something if `environ` is used.
+}
diff --git a/libc-bottom-half/sources/__wasilibc_real.c b/libc-bottom-half/sources/__wasilibc_real.c
new file mode 100644
index 0000000..855a2c6
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_real.c
@@ -0,0 +1,672 @@
+/**
+ * THIS FILE IS AUTO-GENERATED from the following files:
+ * wasi_snapshot_preview1.witx
+ *
+ * To regenerate this file execute:
+ *
+ * cargo run --manifest-path tools/wasi-headers/Cargo.toml generate-libc
+ *
+ * Modifications to this file will cause CI to fail, the code generator tool
+ * must be modified to change this file.
+ */
+
+#include <wasi/api.h>
+#include <string.h>
+
+int32_t __imported_wasi_snapshot_preview1_args_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("args_get")
+));
+
+__wasi_errno_t __wasi_args_get(
+ uint8_t * * argv,
+ uint8_t * argv_buf
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_args_get((int32_t) argv, (int32_t) argv_buf);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_args_sizes_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("args_sizes_get")
+));
+
+__wasi_errno_t __wasi_args_sizes_get(
+ __wasi_size_t *retptr0,
+ __wasi_size_t *retptr1
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_args_sizes_get((int32_t) retptr0, (int32_t) retptr1);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_environ_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("environ_get")
+));
+
+__wasi_errno_t __wasi_environ_get(
+ uint8_t * * environ,
+ uint8_t * environ_buf
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_environ_get((int32_t) environ, (int32_t) environ_buf);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_environ_sizes_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("environ_sizes_get")
+));
+
+__wasi_errno_t __wasi_environ_sizes_get(
+ __wasi_size_t *retptr0,
+ __wasi_size_t *retptr1
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_environ_sizes_get((int32_t) retptr0, (int32_t) retptr1);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_clock_res_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("clock_res_get")
+));
+
+__wasi_errno_t __wasi_clock_res_get(
+ __wasi_clockid_t id,
+ __wasi_timestamp_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_clock_res_get((int32_t) id, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_clock_time_get(int32_t arg0, int64_t arg1, int32_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("clock_time_get")
+));
+
+__wasi_errno_t __wasi_clock_time_get(
+ __wasi_clockid_t id,
+ __wasi_timestamp_t precision,
+ __wasi_timestamp_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_clock_time_get((int32_t) id, (int64_t) precision, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_advise(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_advise")
+));
+
+__wasi_errno_t __wasi_fd_advise(
+ __wasi_fd_t fd,
+ __wasi_filesize_t offset,
+ __wasi_filesize_t len,
+ __wasi_advice_t advice
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_advise((int32_t) fd, (int64_t) offset, (int64_t) len, (int32_t) advice);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_allocate(int32_t arg0, int64_t arg1, int64_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_allocate")
+));
+
+__wasi_errno_t __wasi_fd_allocate(
+ __wasi_fd_t fd,
+ __wasi_filesize_t offset,
+ __wasi_filesize_t len
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_allocate((int32_t) fd, (int64_t) offset, (int64_t) len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_close(int32_t arg0) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_close")
+));
+
+__wasi_errno_t __wasi_fd_close(
+ __wasi_fd_t fd
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_close((int32_t) fd);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_datasync(int32_t arg0) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_datasync")
+));
+
+__wasi_errno_t __wasi_fd_datasync(
+ __wasi_fd_t fd
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_datasync((int32_t) fd);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_fdstat_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_fdstat_get")
+));
+
+__wasi_errno_t __wasi_fd_fdstat_get(
+ __wasi_fd_t fd,
+ __wasi_fdstat_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_fdstat_get((int32_t) fd, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_fdstat_set_flags(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_fdstat_set_flags")
+));
+
+__wasi_errno_t __wasi_fd_fdstat_set_flags(
+ __wasi_fd_t fd,
+ __wasi_fdflags_t flags
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_fdstat_set_flags((int32_t) fd, flags);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_fdstat_set_rights(int32_t arg0, int64_t arg1, int64_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_fdstat_set_rights")
+));
+
+__wasi_errno_t __wasi_fd_fdstat_set_rights(
+ __wasi_fd_t fd,
+ __wasi_rights_t fs_rights_base,
+ __wasi_rights_t fs_rights_inheriting
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_fdstat_set_rights((int32_t) fd, fs_rights_base, fs_rights_inheriting);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_filestat_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_filestat_get")
+));
+
+__wasi_errno_t __wasi_fd_filestat_get(
+ __wasi_fd_t fd,
+ __wasi_filestat_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_filestat_get((int32_t) fd, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_filestat_set_size(int32_t arg0, int64_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_filestat_set_size")
+));
+
+__wasi_errno_t __wasi_fd_filestat_set_size(
+ __wasi_fd_t fd,
+ __wasi_filesize_t size
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_filestat_set_size((int32_t) fd, (int64_t) size);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_filestat_set_times(int32_t arg0, int64_t arg1, int64_t arg2, int32_t arg3) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_filestat_set_times")
+));
+
+__wasi_errno_t __wasi_fd_filestat_set_times(
+ __wasi_fd_t fd,
+ __wasi_timestamp_t atim,
+ __wasi_timestamp_t mtim,
+ __wasi_fstflags_t fst_flags
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_filestat_set_times((int32_t) fd, (int64_t) atim, (int64_t) mtim, fst_flags);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_pread(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_pread")
+));
+
+__wasi_errno_t __wasi_fd_pread(
+ __wasi_fd_t fd,
+ const __wasi_iovec_t *iovs,
+ size_t iovs_len,
+ __wasi_filesize_t offset,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_pread((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int64_t) offset, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_prestat_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_prestat_get")
+));
+
+__wasi_errno_t __wasi_fd_prestat_get(
+ __wasi_fd_t fd,
+ __wasi_prestat_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_prestat_get((int32_t) fd, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_prestat_dir_name(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_prestat_dir_name")
+));
+
+__wasi_errno_t __wasi_fd_prestat_dir_name(
+ __wasi_fd_t fd,
+ uint8_t * path,
+ __wasi_size_t path_len
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_prestat_dir_name((int32_t) fd, (int32_t) path, (int32_t) path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_pwrite(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_pwrite")
+));
+
+__wasi_errno_t __wasi_fd_pwrite(
+ __wasi_fd_t fd,
+ const __wasi_ciovec_t *iovs,
+ size_t iovs_len,
+ __wasi_filesize_t offset,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_pwrite((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int64_t) offset, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_read(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_read")
+));
+
+__wasi_errno_t __wasi_fd_read(
+ __wasi_fd_t fd,
+ const __wasi_iovec_t *iovs,
+ size_t iovs_len,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_read((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_readdir(int32_t arg0, int32_t arg1, int32_t arg2, int64_t arg3, int32_t arg4) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_readdir")
+));
+
+__wasi_errno_t __wasi_fd_readdir(
+ __wasi_fd_t fd,
+ uint8_t * buf,
+ __wasi_size_t buf_len,
+ __wasi_dircookie_t cookie,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_readdir((int32_t) fd, (int32_t) buf, (int32_t) buf_len, (int64_t) cookie, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_renumber(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_renumber")
+));
+
+__wasi_errno_t __wasi_fd_renumber(
+ __wasi_fd_t fd,
+ __wasi_fd_t to
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_renumber((int32_t) fd, (int32_t) to);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_seek(int32_t arg0, int64_t arg1, int32_t arg2, int32_t arg3) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_seek")
+));
+
+__wasi_errno_t __wasi_fd_seek(
+ __wasi_fd_t fd,
+ __wasi_filedelta_t offset,
+ __wasi_whence_t whence,
+ __wasi_filesize_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_seek((int32_t) fd, offset, (int32_t) whence, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_sync(int32_t arg0) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_sync")
+));
+
+__wasi_errno_t __wasi_fd_sync(
+ __wasi_fd_t fd
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_sync((int32_t) fd);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_tell(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_tell")
+));
+
+__wasi_errno_t __wasi_fd_tell(
+ __wasi_fd_t fd,
+ __wasi_filesize_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_tell((int32_t) fd, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_fd_write(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("fd_write")
+));
+
+__wasi_errno_t __wasi_fd_write(
+ __wasi_fd_t fd,
+ const __wasi_ciovec_t *iovs,
+ size_t iovs_len,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_fd_write((int32_t) fd, (int32_t) iovs, (int32_t) iovs_len, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_create_directory(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_create_directory")
+));
+
+__wasi_errno_t __wasi_path_create_directory(
+ __wasi_fd_t fd,
+ const char *path
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_create_directory((int32_t) fd, (int32_t) path, (int32_t) path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_filestat_get(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_filestat_get")
+));
+
+__wasi_errno_t __wasi_path_filestat_get(
+ __wasi_fd_t fd,
+ __wasi_lookupflags_t flags,
+ const char *path,
+ __wasi_filestat_t *retptr0
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_filestat_get((int32_t) fd, flags, (int32_t) path, (int32_t) path_len, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_filestat_set_times(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int64_t arg4, int64_t arg5, int32_t arg6) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_filestat_set_times")
+));
+
+__wasi_errno_t __wasi_path_filestat_set_times(
+ __wasi_fd_t fd,
+ __wasi_lookupflags_t flags,
+ const char *path,
+ __wasi_timestamp_t atim,
+ __wasi_timestamp_t mtim,
+ __wasi_fstflags_t fst_flags
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_filestat_set_times((int32_t) fd, flags, (int32_t) path, (int32_t) path_len, (int64_t) atim, (int64_t) mtim, fst_flags);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_link(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5, int32_t arg6) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_link")
+));
+
+__wasi_errno_t __wasi_path_link(
+ __wasi_fd_t old_fd,
+ __wasi_lookupflags_t old_flags,
+ const char *old_path,
+ __wasi_fd_t new_fd,
+ const char *new_path
+){
+ size_t old_path_len = strlen(old_path);
+ size_t new_path_len = strlen(new_path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_link((int32_t) old_fd, old_flags, (int32_t) old_path, (int32_t) old_path_len, (int32_t) new_fd, (int32_t) new_path, (int32_t) new_path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_open(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int64_t arg5, int64_t arg6, int32_t arg7, int32_t arg8) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_open")
+));
+
+__wasi_errno_t __wasi_path_open(
+ __wasi_fd_t fd,
+ __wasi_lookupflags_t dirflags,
+ const char *path,
+ __wasi_oflags_t oflags,
+ __wasi_rights_t fs_rights_base,
+ __wasi_rights_t fs_rights_inheriting,
+ __wasi_fdflags_t fdflags,
+ __wasi_fd_t *retptr0
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_open((int32_t) fd, dirflags, (int32_t) path, (int32_t) path_len, oflags, fs_rights_base, fs_rights_inheriting, fdflags, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_readlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_readlink")
+));
+
+__wasi_errno_t __wasi_path_readlink(
+ __wasi_fd_t fd,
+ const char *path,
+ uint8_t * buf,
+ __wasi_size_t buf_len,
+ __wasi_size_t *retptr0
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_readlink((int32_t) fd, (int32_t) path, (int32_t) path_len, (int32_t) buf, (int32_t) buf_len, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_remove_directory(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_remove_directory")
+));
+
+__wasi_errno_t __wasi_path_remove_directory(
+ __wasi_fd_t fd,
+ const char *path
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_remove_directory((int32_t) fd, (int32_t) path, (int32_t) path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_rename(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_rename")
+));
+
+__wasi_errno_t __wasi_path_rename(
+ __wasi_fd_t fd,
+ const char *old_path,
+ __wasi_fd_t new_fd,
+ const char *new_path
+){
+ size_t old_path_len = strlen(old_path);
+ size_t new_path_len = strlen(new_path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_rename((int32_t) fd, (int32_t) old_path, (int32_t) old_path_len, (int32_t) new_fd, (int32_t) new_path, (int32_t) new_path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_symlink(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_symlink")
+));
+
+__wasi_errno_t __wasi_path_symlink(
+ const char *old_path,
+ __wasi_fd_t fd,
+ const char *new_path
+){
+ size_t old_path_len = strlen(old_path);
+ size_t new_path_len = strlen(new_path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_symlink((int32_t) old_path, (int32_t) old_path_len, (int32_t) fd, (int32_t) new_path, (int32_t) new_path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_path_unlink_file(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("path_unlink_file")
+));
+
+__wasi_errno_t __wasi_path_unlink_file(
+ __wasi_fd_t fd,
+ const char *path
+){
+ size_t path_len = strlen(path);
+ int32_t ret = __imported_wasi_snapshot_preview1_path_unlink_file((int32_t) fd, (int32_t) path, (int32_t) path_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_poll_oneoff(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("poll_oneoff")
+));
+
+__wasi_errno_t __wasi_poll_oneoff(
+ const __wasi_subscription_t * in,
+ __wasi_event_t * out,
+ __wasi_size_t nsubscriptions,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_poll_oneoff((int32_t) in, (int32_t) out, (int32_t) nsubscriptions, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+_Noreturn void __imported_wasi_snapshot_preview1_proc_exit(int32_t arg0) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("proc_exit")
+));
+
+_Noreturn void __wasi_proc_exit(
+ __wasi_exitcode_t rval
+){
+ __imported_wasi_snapshot_preview1_proc_exit((int32_t) rval);
+}
+
+int32_t __imported_wasi_snapshot_preview1_sched_yield() __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("sched_yield")
+));
+
+__wasi_errno_t __wasi_sched_yield(
+ void
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_sched_yield();
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_random_get(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("random_get")
+));
+
+__wasi_errno_t __wasi_random_get(
+ uint8_t * buf,
+ __wasi_size_t buf_len
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_random_get((int32_t) buf, (int32_t) buf_len);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_sock_accept(int32_t arg0, int32_t arg1, int32_t arg2) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("sock_accept")
+));
+
+__wasi_errno_t __wasi_sock_accept(
+ __wasi_fd_t fd,
+ __wasi_fdflags_t flags,
+ __wasi_fd_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_sock_accept((int32_t) fd, flags, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_sock_recv(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4, int32_t arg5) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("sock_recv")
+));
+
+__wasi_errno_t __wasi_sock_recv(
+ __wasi_fd_t fd,
+ const __wasi_iovec_t *ri_data,
+ size_t ri_data_len,
+ __wasi_riflags_t ri_flags,
+ __wasi_size_t *retptr0,
+ __wasi_roflags_t *retptr1
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_sock_recv((int32_t) fd, (int32_t) ri_data, (int32_t) ri_data_len, ri_flags, (int32_t) retptr0, (int32_t) retptr1);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_sock_send(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("sock_send")
+));
+
+__wasi_errno_t __wasi_sock_send(
+ __wasi_fd_t fd,
+ const __wasi_ciovec_t *si_data,
+ size_t si_data_len,
+ __wasi_siflags_t si_flags,
+ __wasi_size_t *retptr0
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_sock_send((int32_t) fd, (int32_t) si_data, (int32_t) si_data_len, (int32_t) si_flags, (int32_t) retptr0);
+ return (uint16_t) ret;
+}
+
+int32_t __imported_wasi_snapshot_preview1_sock_shutdown(int32_t arg0, int32_t arg1) __attribute__((
+ __import_module__("wasi_snapshot_preview1"),
+ __import_name__("sock_shutdown")
+));
+
+__wasi_errno_t __wasi_sock_shutdown(
+ __wasi_fd_t fd,
+ __wasi_sdflags_t how
+){
+ int32_t ret = __imported_wasi_snapshot_preview1_sock_shutdown((int32_t) fd, how);
+ return (uint16_t) ret;
+}
+
+#ifdef _REENTRANT
+int32_t __imported_wasi_thread_spawn(int32_t arg0) __attribute__((
+ __import_module__("wasi"),
+ __import_name__("thread_spawn")
+));
+
+__wasi_errno_t __wasi_thread_spawn(void* start_arg) {
+ int32_t ret = __imported_wasi_thread_spawn((int32_t) start_arg);
+ return (uint16_t) ret;
+}
+#endif
diff --git a/libc-bottom-half/sources/__wasilibc_rmdirat.c b/libc-bottom-half/sources/__wasilibc_rmdirat.c
new file mode 100644
index 0000000..b2b906a
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_rmdirat.c
@@ -0,0 +1,12 @@
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+
+int __wasilibc_nocwd___wasilibc_rmdirat(int fd, const char *path) {
+ __wasi_errno_t error = __wasi_path_remove_directory(fd, path);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/sources/__wasilibc_tell.c b/libc-bottom-half/sources/__wasilibc_tell.c
new file mode 100644
index 0000000..358d0ca
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_tell.c
@@ -0,0 +1,14 @@
+#include <wasi/api.h>
+#include <errno.h>
+
+off_t __wasilibc_tell(int fildes) {
+ __wasi_filesize_t offset;
+ __wasi_errno_t error = __wasi_fd_tell(fildes, &offset);
+ if (error != 0) {
+ // lseek returns ESPIPE on when called on a pipe, socket, or fifo,
+ // which on WASI would translate into ENOTCAPABLE.
+ errno = error == ENOTCAPABLE ? ESPIPE : error;
+ return -1;
+ }
+ return offset;
+}
diff --git a/libc-bottom-half/sources/__wasilibc_unlinkat.c b/libc-bottom-half/sources/__wasilibc_unlinkat.c
new file mode 100644
index 0000000..8b4f6b5
--- /dev/null
+++ b/libc-bottom-half/sources/__wasilibc_unlinkat.c
@@ -0,0 +1,12 @@
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <errno.h>
+
+int __wasilibc_nocwd___wasilibc_unlinkat(int fd, const char *path) {
+ __wasi_errno_t error = __wasi_path_unlink_file(fd, path);
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/libc-bottom-half/sources/abort.c b/libc-bottom-half/sources/abort.c
new file mode 100644
index 0000000..95fe2be
--- /dev/null
+++ b/libc-bottom-half/sources/abort.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+void abort(void) {
+ // wasm doesn't support signals, so just trap to halt the program.
+ __builtin_trap();
+}
diff --git a/libc-bottom-half/sources/accept.c b/libc-bottom-half/sources/accept.c
new file mode 100644
index 0000000..902e731
--- /dev/null
+++ b/libc-bottom-half/sources/accept.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: BSD-2-Clause
+
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <wasi/api.h>
+#include <errno.h>
+#include <string.h>
+
+int accept(int socket, struct sockaddr *restrict addr, socklen_t *restrict addrlen) {
+ int ret = -1;
+
+ __wasi_errno_t error = __wasi_sock_accept(socket, 0, &ret);
+
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Clear sockaddr to indicate undefined address
+ memset(addr, 0, *addrlen);
+ // might be AF_UNIX or AF_INET
+ addr->sa_family = AF_UNSPEC;
+ *addrlen = sizeof(struct sockaddr);
+
+ return ret;
+}
+
+int accept4(int socket, struct sockaddr *restrict addr, socklen_t *restrict addrlen, int flags) {
+ int ret = -1;
+
+ if (flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ __wasi_errno_t error = __wasi_sock_accept(socket, (flags & SOCK_NONBLOCK) ? __WASI_FDFLAGS_NONBLOCK : 0, &ret);
+
+ if (error != 0) {
+ errno = error;
+ return -1;
+ }
+
+ // Clear sockaddr to indicate undefined address
+ memset(addr, 0, *addrlen);
+ // might be AF_UNIX or AF_INET
+ addr->sa_family = AF_UNSPEC;
+ *addrlen = sizeof(struct sockaddr);
+
+ return ret;
+}
diff --git a/libc-bottom-half/sources/at_fdcwd.c b/libc-bottom-half/sources/at_fdcwd.c
new file mode 100644
index 0000000..c83797d
--- /dev/null
+++ b/libc-bottom-half/sources/at_fdcwd.c
@@ -0,0 +1,141 @@
+// Handle AT_FDCWD and absolute paths for the *at functions.
+//
+// In the case of an AT_FDCWD file descriptor or an absolute path, call the
+// corresponding non-`at` function. This will send it through the libpreopen
+// wrappers to convert the path into a directory file descriptor and relative
+// path before translating it into the corresponding `__wasilibc_nocwd_*at`
+// function, which then calls the appropriate WASI function.
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <wasi/libc.h>
+#include <wasi/libc-nocwd.h>
+
+// If the platform doesn't define O_TMPFILE, we don't need to worry about it.
+#ifndef O_TMPFILE
+#define O_TMPFILE 0
+#endif
+
+int openat(int dirfd, const char *pathname, int flags, ...) {
+ if (dirfd == AT_FDCWD || pathname[0] == '/') {
+ return open(pathname, flags);
+ }
+
+ return __wasilibc_nocwd_openat_nomode(dirfd, pathname, flags);
+}
+
+int symlinkat(const char *target, int dirfd, const char *linkpath) {
+ if (dirfd == AT_FDCWD || linkpath[0] == '/') {
+ return symlink(target, linkpath);
+ }
+
+ return __wasilibc_nocwd_symlinkat(target, dirfd, linkpath);
+}
+
+ssize_t readlinkat(int dirfd, const char *__restrict pathname, char *__restrict buf, size_t bufsiz) {
+ if (dirfd == AT_FDCWD || pathname[0] == '/') {
+ return readlink(pathname, buf, bufsiz);
+ }
+
+ return __wasilibc_nocwd_readlinkat(dirfd, pathname, buf, bufsiz);
+}
+
+int mkdirat(int dirfd, const char *pathname, mode_t mode) {
+ if (dirfd == AT_FDCWD || pathname[0] == '/') {
+ return mkdir(pathname, mode);
+ }
+
+ return __wasilibc_nocwd_mkdirat_nomode(dirfd, pathname);
+}
+
+DIR *opendirat(int dirfd, const char *path) {
+ if (dirfd == AT_FDCWD || path[0] == '/') {
+ return opendir(path);
+ }
+
+ return __wasilibc_nocwd_opendirat(dirfd, path);
+}
+
+int scandirat(int dirfd, const char *dirp, struct dirent ***namelist,
+ int (*filter)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **)) {
+ if (dirfd == AT_FDCWD || dirp[0] == '/') {
+ return scandir(dirp, namelist, filter, compar);
+ }
+
+ return __wasilibc_nocwd_scandirat(dirfd, dirp, namelist, filter, compar);
+}
+
+int faccessat(int dirfd, const char *pathname, int mode, int flags) {
+ if (dirfd == AT_FDCWD || pathname[0] == '/') {
+ return __wasilibc_access(pathname, mode, flags);
+ }
+
+ return __wasilibc_nocwd_faccessat(dirfd, pathname, mode, flags);
+}
+
+int fstatat(int dirfd, const char *__restrict pathname, struct stat *__restrict statbuf, int flags) {
+ if (dirfd == AT_FDCWD || pathname[0] == '/') {
+ return __wasilibc_stat(pathname, statbuf, flags);
+ }
+
+ return __wasilibc_nocwd_fstatat(dirfd, pathname, statbuf, flags);
+}
+
+int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags) {
+ if (dirfd == AT_FDCWD || pathname[0] == '/') {
+ return __wasilibc_utimens(pathname, times, flags);
+ }
+
+ return __wasilibc_nocwd_utimensat(dirfd, pathname, times, flags);
+}
+
+int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
+ if ((olddirfd == AT_FDCWD || oldpath[0] == '/') &&
+ (newdirfd == AT_FDCWD || newpath[0] == '/')) {
+ return __wasilibc_link(oldpath, newpath, flags);
+ }
+ if (olddirfd == AT_FDCWD || oldpath[0] == '/') {
+ return __wasilibc_link_newat(oldpath, newdirfd, newpath, flags);
+ }
+ if (newdirfd == AT_FDCWD || newpath[0] == '/') {
+ return __wasilibc_link_oldat(olddirfd, oldpath, newpath, flags);
+ }
+
+ return __wasilibc_nocwd_linkat(olddirfd, oldpath, newdirfd, newpath, flags);
+}
+
+int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ if ((olddirfd == AT_FDCWD || oldpath[0] == '/') &&
+ (newdirfd == AT_FDCWD || newpath[0] == '/')) {
+ return rename(oldpath, newpath);
+ }
+ if (olddirfd == AT_FDCWD || oldpath[0] == '/') {
+ return __wasilibc_rename_newat(oldpath, newdirfd, newpath);
+ }
+ if (newdirfd == AT_FDCWD || newpath[0] == '/') {
+ return __wasilibc_rename_oldat(olddirfd, oldpath, newpath);
+ }
+
+ return __wasilibc_nocwd_renameat(olddirfd, oldpath, newdirfd, newpath);
+}
+
+int __wasilibc_unlinkat(int dirfd, const char *path) {
+ if (dirfd == AT_FDCWD || path[0] == '/') {
+ return unlink(path);
+ }
+
+ return __wasilibc_nocwd___wasilibc_unlinkat(dirfd, path);
+}
+
+int __wasilibc_rmdirat(int dirfd, const char *path) {
+ if (dirfd == AT_FDCWD || path[0] == '/') {
+ return rmdir(path);
+ }
+
+ return __wasilibc_nocwd___wasilibc_rmdirat(dirfd, path);
+}
diff --git a/libc-bottom-half/sources/chdir.c b/libc-bottom-half/sources/chdir.c
new file mode 100644
index 0000000..37c95a4
--- /dev/null
+++ b/libc-bottom-half/sources/chdir.c
@@ -0,0 +1,171 @@
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <wasi/libc-find-relpath.h>
+#include <wasi/libc.h>
+
+#ifdef _REENTRANT
+void __wasilibc_cwd_lock(void);
+void __wasilibc_cwd_unlock(void);
+#else
+#define __wasilibc_cwd_lock() (void)0
+#define __wasilibc_cwd_unlock() (void)0
+#endif
+extern char *__wasilibc_cwd;
+static int __wasilibc_cwd_mallocd = 0;
+
+int chdir(const char *path)
+{
+ static char *relative_buf = NULL;
+ static size_t relative_buf_len = 0;
+
+ // Find a preopen'd directory as well as a relative path we're anchored
+ // from which we're changing directories to.
+ const char *abs;
+ int parent_fd = __wasilibc_find_relpath_alloc(path, &abs, &relative_buf, &relative_buf_len, 1);
+ if (parent_fd == -1)
+ return -1;
+
+ // Make sure that this directory we're accessing is indeed a directory.
+ struct stat dirinfo;
+ int ret = fstatat(parent_fd, relative_buf, &dirinfo, 0);
+ if (ret == -1)
+ return -1;
+ if (!S_ISDIR(dirinfo.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ // Create a string that looks like:
+ //
+ // __wasilibc_cwd = "/" + abs + "/" + relative_buf
+ //
+ // If `relative_buf` is equal to "." or `abs` is equal to the empty string,
+ // however, we skip that part and the middle slash.
+ size_t abs_len = strlen(abs);
+ int copy_relative = strcmp(relative_buf, ".") != 0;
+ int mid = copy_relative && abs[0] != 0;
+ char *new_cwd = malloc(1 + abs_len + mid + (copy_relative ? strlen(relative_buf) : 0) + 1);
+ if (new_cwd == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ new_cwd[0] = '/';
+ strcpy(new_cwd + 1, abs);
+ if (mid)
+ new_cwd[1 + abs_len] = '/';
+ if (copy_relative)
+ strcpy(new_cwd + 1 + abs_len + mid, relative_buf);
+
+ // And set our new malloc'd buffer into the global cwd, freeing the
+ // previous one if necessary.
+ __wasilibc_cwd_lock();
+ char *prev_cwd = __wasilibc_cwd;
+ __wasilibc_cwd = new_cwd;
+ __wasilibc_cwd_unlock();
+ if (__wasilibc_cwd_mallocd)
+ free(prev_cwd);
+ __wasilibc_cwd_mallocd = 1;
+ return 0;
+}
+
+static const char *make_absolute(const char *path) {
+ static char *make_absolute_buf = NULL;
+ static size_t make_absolute_len = 0;
+
+ // If this path is absolute, then we return it as-is.
+ if (path[0] == '/') {
+ return path;
+ }
+
+#ifndef _REENTRANT
+ // If the path is empty, or points to the current directory, then return
+ // the current directory.
+ if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) {
+ return __wasilibc_cwd;
+ }
+#endif
+
+ // If the path starts with `./` then we won't be appending that to the cwd.
+ if (path[0] == '.' && path[1] == '/')
+ path += 2;
+
+ // Otherwise we'll take the current directory, add a `/`, and then add the
+ // input `path`. Note that this doesn't do any normalization (like removing
+ // `/./`).
+ __wasilibc_cwd_lock();
+ size_t cwd_len = strlen(__wasilibc_cwd);
+ size_t path_len = path ? strlen(path) : 0;
+ __wasilibc_cwd_unlock();
+ int need_slash = __wasilibc_cwd[cwd_len - 1] == '/' ? 0 : 1;
+ size_t alloc_len = cwd_len + path_len + 1 + need_slash;
+ if (alloc_len > make_absolute_len) {
+ char *tmp = realloc(make_absolute_buf, alloc_len);
+ if (tmp == NULL) {
+ __wasilibc_cwd_unlock();
+ return NULL;
+ }
+ make_absolute_buf = tmp;
+ make_absolute_len = alloc_len;
+ }
+ strcpy(make_absolute_buf, __wasilibc_cwd);
+ __wasilibc_cwd_unlock();
+
+#ifdef _REENTRANT
+ if (path[0] == 0 || !strcmp(path, ".") || !strcmp(path, "./")) {
+ return make_absolute_buf;
+ }
+#endif
+
+ if (need_slash)
+ strcpy(make_absolute_buf + cwd_len, "/");
+ strcpy(make_absolute_buf + cwd_len + need_slash, path);
+ return make_absolute_buf;
+}
+
+// Helper function defined only in this object file and weakly referenced from
+// `preopens.c` and `posix.c` This function isn't necessary unless `chdir` is
+// pulled in because all paths are otherwise absolute or relative to the root.
+int __wasilibc_find_relpath_alloc(
+ const char *path,
+ const char **abs_prefix,
+ char **relative_buf,
+ size_t *relative_buf_len,
+ int can_realloc
+) {
+ // First, make our path absolute taking the cwd into account.
+ const char *abspath = make_absolute(path);
+ if (abspath == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ // Next use our absolute path and split it. Find the preopened `fd` parent
+ // directory and set `abs_prefix`. Next up we'll be trying to fit `rel`
+ // into `relative_buf`.
+ const char *rel;
+ int fd = __wasilibc_find_abspath(abspath, abs_prefix, &rel);
+ if (fd == -1)
+ return -1;
+
+ size_t rel_len = strlen(rel);
+ if (*relative_buf_len < rel_len + 1) {
+ if (!can_realloc) {
+ errno = ERANGE;
+ return -1;
+ }
+ char *tmp = realloc(*relative_buf, rel_len + 1);
+ if (tmp == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ *relative_buf = tmp;
+ *relative_buf_len = rel_len + 1;
+ }
+ strcpy(*relative_buf, rel);
+ return fd;
+}
diff --git a/libc-bottom-half/sources/complex-builtins.c b/libc-bottom-half/sources/complex-builtins.c
new file mode 100644
index 0000000..971d57e
--- /dev/null
+++ b/libc-bottom-half/sources/complex-builtins.c
@@ -0,0 +1,29 @@
+// Each of the following complex functions can be implemented with a single
+// wasm instruction, so use that implementation rather than the portable
+// one in libm.
+
+#include <complex.h>
+
+float (crealf)(float _Complex x) {
+ return __builtin_crealf(x);
+}
+
+double (creal)(double _Complex x) {
+ return __builtin_creal(x);
+}
+
+long double (creall)(long double _Complex x) {
+ return __builtin_creall(x);
+}
+
+float (cimagf)(float _Complex x) {
+ return __builtin_cimagf(x);
+}
+
+double (cimag)(double _Complex x) {
+ return __builtin_cimag(x);
+}
+
+long double (cimagl)(long double _Complex x) {
+ return __builtin_cimagl(x);
+}
diff --git a/libc-bottom-half/sources/environ.c b/libc-bottom-half/sources/environ.c
new file mode 100644
index 0000000..50d60de
--- /dev/null
+++ b/libc-bottom-half/sources/environ.c
@@ -0,0 +1,31 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <wasi/api.h>
+#include <wasi/libc.h>
+#include <wasi/libc-environ.h>
+
+// If the program does use `environ`, it'll get this version of
+// `__wasilibc_environ`, which is initialized with a constructor function, so
+// that it's initialized whenever user code might want to access it.
+char **__wasilibc_environ;
+weak_alias(__wasilibc_environ, _environ);
+weak_alias(__wasilibc_environ, environ);
+
+// We define this function here in the same source file as
+// `__wasilibc_environ`, so that this function is called in iff environment
+// variable support is used.
+// Concerning the 50 -- levels up to 100 are reserved for the implementation,
+// so we an arbitrary number in the middle of the range to allow other
+// reserved things to go before or after.
+__attribute__((constructor(50)))
+static void __wasilibc_initialize_environ_eagerly(void) {
+ __wasilibc_initialize_environ();
+}
+
+// See the comments in libc-environ.h.
+void __wasilibc_maybe_reinitialize_environ_eagerly(void) {
+ // This translation unit is linked in if `environ` is used, meaning we need
+ // to eagerly reinitialize the environment variables.
+ __wasilibc_initialize_environ();
+}
diff --git a/libc-bottom-half/sources/errno.c b/libc-bottom-half/sources/errno.c
new file mode 100644
index 0000000..4e98a5b
--- /dev/null
+++ b/libc-bottom-half/sources/errno.c
@@ -0,0 +1,5 @@
+#include <errno.h>
+
+// These values are used by reference-sysroot's dlmalloc.
+const int __EINVAL = EINVAL;
+const int __ENOMEM = ENOMEM;
diff --git a/libc-bottom-half/sources/getcwd.c b/libc-bottom-half/sources/getcwd.c
new file mode 100644
index 0000000..3b1ce70
--- /dev/null
+++ b/libc-bottom-half/sources/getcwd.c
@@ -0,0 +1,39 @@
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include "lock.h"
+
+char *__wasilibc_cwd = "/";
+
+#ifdef _REENTRANT
+static volatile int lock[1];
+void __wasilibc_cwd_lock(void) { LOCK(lock); }
+void __wasilibc_cwd_unlock(void) { UNLOCK(lock); }
+#else
+#define __wasilibc_cwd_lock() (void)0
+#define __wasilibc_cwd_unlock() (void)0
+#endif
+
+char *getcwd(char *buf, size_t size)
+{
+ __wasilibc_cwd_lock();
+ if (!buf) {
+ buf = strdup(__wasilibc_cwd);
+ if (!buf) {
+ errno = ENOMEM;
+ __wasilibc_cwd_unlock();
+ return NULL;
+ }
+ } else {
+ size_t len = strlen(__wasilibc_cwd);
+ if (size < len + 1) {
+ errno = ERANGE;
+ __wasilibc_cwd_unlock();
+ return NULL;
+ }
+ strcpy(buf, __wasilibc_cwd);
+ }
+ __wasilibc_cwd_unlock();
+ return buf;
+}
+
diff --git a/libc-bottom-half/sources/getentropy.c b/libc-bottom-half/sources/getentropy.c
new file mode 100644
index 0000000..e540e7e
--- /dev/null
+++ b/libc-bottom-half/sources/getentropy.c
@@ -0,0 +1,20 @@
+#include <errno.h>
+#include <unistd.h>
+#include <wasi/api.h>
+
+int __getentropy(void *buffer, size_t len) {
+ if (len > 256) {
+ errno = EIO;
+ return -1;
+ }
+
+ int r = __wasi_random_get(buffer, len);
+
+ if (r != 0) {
+ errno = r;
+ return -1;
+ }
+
+ return 0;
+}
+weak_alias(__getentropy, getentropy);
diff --git a/libc-bottom-half/sources/isatty.c b/libc-bottom-half/sources/isatty.c
new file mode 100644
index 0000000..c6f8662
--- /dev/null
+++ b/libc-bottom-half/sources/isatty.c
@@ -0,0 +1,22 @@
+#include <wasi/api.h>
+#include <__errno.h>
+#include <__function___isatty.h>
+
+int __isatty(int fd) {
+ __wasi_fdstat_t statbuf;
+ int r = __wasi_fd_fdstat_get(fd, &statbuf);
+ if (r != 0) {
+ errno = r;
+ return 0;
+ }
+
+ // A tty is a character device that we can't seek or tell on.
+ if (statbuf.fs_filetype != __WASI_FILETYPE_CHARACTER_DEVICE ||
+ (statbuf.fs_rights_base & (__WASI_RIGHTS_FD_SEEK | __WASI_RIGHTS_FD_TELL)) != 0) {
+ errno = __WASI_ERRNO_NOTTY;
+ return 0;
+ }
+
+ return 1;
+}
+extern __typeof(__isatty) isatty __attribute__((weak, alias("__isatty")));
diff --git a/libc-bottom-half/sources/math/fmin-fmax.c b/libc-bottom-half/sources/math/fmin-fmax.c
new file mode 100644
index 0000000..9293798
--- /dev/null
+++ b/libc-bottom-half/sources/math/fmin-fmax.c
@@ -0,0 +1,34 @@
+// Wasm's `min` and `max` operators implement the IEEE 754-2019
+// `minimum` and `maximum` operations, meaning that given a choice
+// between NaN and a number, they return NaN. This differs from
+// the C standard library's `fmin` and `fmax` functions, which
+// return the number. However, we can still use wasm's builtins
+// by handling the NaN cases explicitly, and it still turns out
+// to be faster than doing the whole operation in
+// target-independent C. And, it's smaller.
+
+#include <math.h>
+
+float fminf(float x, float y) {
+ if (isnan(x)) return y;
+ if (isnan(y)) return x;
+ return __builtin_wasm_min_f32(x, y);
+}
+
+float fmaxf(float x, float y) {
+ if (isnan(x)) return y;
+ if (isnan(y)) return x;
+ return __builtin_wasm_max_f32(x, y);
+}
+
+double fmin(double x, double y) {
+ if (isnan(x)) return y;
+ if (isnan(y)) return x;
+ return __builtin_wasm_min_f64(x, y);
+}
+
+double fmax(double x, double y) {
+ if (isnan(x)) return y;
+ if (isnan(y)) return x;
+ return __builtin_wasm_max_f64(x, y);
+}
diff --git a/libc-bottom-half/sources/math/math-builtins.c b/libc-bottom-half/sources/math/math-builtins.c
new file mode 100644
index 0000000..a5eb7cd
--- /dev/null
+++ b/libc-bottom-half/sources/math/math-builtins.c
@@ -0,0 +1,69 @@
+// Each of the following math functions can be implemented with a single
+// wasm instruction, so use that implementation rather than the portable
+// one in libm.
+
+#include <math.h>
+
+float fabsf(float x) {
+ return __builtin_fabsf(x);
+}
+
+double fabs(double x) {
+ return __builtin_fabs(x);
+}
+
+float sqrtf(float x) {
+ return __builtin_sqrtf(x);
+}
+
+double sqrt(double x) {
+ return __builtin_sqrt(x);
+}
+
+float copysignf(float x, float y) {
+ return __builtin_copysignf(x, y);
+}
+
+double copysign(double x, double y) {
+ return __builtin_copysign(x, y);
+}
+
+float ceilf(float x) {
+ return __builtin_ceilf(x);
+}
+
+double ceil(double x) {
+ return __builtin_ceil(x);
+}
+
+float floorf(float x) {
+ return __builtin_floorf(x);
+}
+
+double floor(double x) {
+ return __builtin_floor(x);
+}
+
+float truncf(float x) {
+ return __builtin_truncf(x);
+}
+
+double trunc(double x) {
+ return __builtin_trunc(x);
+}
+
+float nearbyintf(float x) {
+ return __builtin_nearbyintf(x);
+}
+
+double nearbyint(double x) {
+ return __builtin_nearbyint(x);
+}
+
+float rintf(float x) {
+ return __builtin_rintf(x);
+}
+
+double rint(double x) {
+ return __builtin_rint(x);
+}
diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c
new file mode 100644
index 0000000..35dd99b
--- /dev/null
+++ b/libc-bottom-half/sources/posix.c
@@ -0,0 +1,449 @@
+//! POSIX-like functions supporting absolute path arguments, implemented in
+//! terms of `__wasilibc_find_relpath` and `*at`-style functions.
+
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utime.h>
+#include <wasi/libc.h>
+#include <wasi/libc-find-relpath.h>
+#include <wasi/libc-nocwd.h>
+
+static int find_relpath2(
+ const char *path,
+ char **relative,
+ size_t *relative_len
+) {
+ // See comments in `preopens.c` for what this trick is doing.
+ const char *abs;
+ if (__wasilibc_find_relpath_alloc)
+ return __wasilibc_find_relpath_alloc(path, &abs, relative, relative_len, 1);
+ return __wasilibc_find_relpath(path, &abs, relative, *relative_len);
+}
+
+// Helper to call `__wasilibc_find_relpath` and return an already-managed
+// pointer for the `relative` path. This function is not reentrant since the
+// `relative` pointer will point to static data that cannot be reused until
+// `relative` is no longer used.
+static int find_relpath(const char *path, char **relative) {
+ static __thread char *relative_buf = NULL;
+ static __thread size_t relative_buf_len = 0;
+ int fd = find_relpath2(path, &relative_buf, &relative_buf_len);
+ // find_relpath2 can update relative_buf, so assign it after the call
+ *relative = relative_buf;
+ return fd;
+}
+
+// same as `find_relpath`, but uses another set of static variables to cache
+static int find_relpath_alt(const char *path, char **relative) {
+ static __thread char *relative_buf = NULL;
+ static __thread size_t relative_buf_len = 0;
+ int fd = find_relpath2(path, &relative_buf, &relative_buf_len);
+ // find_relpath2 can update relative_buf, so assign it after the call
+ *relative = relative_buf;
+ return fd;
+}
+
+int open(const char *path, int oflag, ...) {
+ // WASI libc's `openat` ignores the mode argument, so call a special
+ // entrypoint which avoids the varargs calling convention.
+ return __wasilibc_open_nomode(path, oflag);
+}
+
+// See the documentation in libc.h
+int __wasilibc_open_nomode(const char *path, int oflag) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_openat_nomode(dirfd, relative_path, oflag);
+}
+
+int access(const char *path, int amode) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_faccessat(dirfd, relative_path, amode, 0);
+}
+
+ssize_t readlink(
+ const char *restrict path,
+ char *restrict buf,
+ size_t bufsize)
+{
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_readlinkat(dirfd, relative_path, buf, bufsize);
+}
+
+int stat(const char *restrict path, struct stat *restrict buf) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_fstatat(dirfd, relative_path, buf, 0);
+}
+
+int lstat(const char *restrict path, struct stat *restrict buf) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_fstatat(dirfd, relative_path, buf, AT_SYMLINK_NOFOLLOW);
+}
+
+int utime(const char *path, const struct utimbuf *times) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_utimensat(
+ dirfd, relative_path,
+ times ? ((struct timespec [2]) {
+ { .tv_sec = times->actime },
+ { .tv_sec = times->modtime }
+ })
+ : NULL,
+ 0);
+}
+
+int utimes(const char *path, const struct timeval times[2]) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_utimensat(
+ dirfd, relative_path,
+ times ? ((struct timespec [2]) {
+ { .tv_sec = times[0].tv_sec,
+ .tv_nsec = times[0].tv_usec * 1000 },
+ { .tv_sec = times[1].tv_sec,
+ .tv_nsec = times[1].tv_usec * 1000 },
+ })
+ : NULL,
+ 0);
+}
+
+int unlink(const char *path) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ // `unlinkat` imports `__wasi_path_remove_directory` even when
+ // `AT_REMOVEDIR` isn't passed. Instead, use a specialized function which
+ // just imports `__wasi_path_unlink_file`.
+ return __wasilibc_nocwd___wasilibc_unlinkat(dirfd, relative_path);
+}
+
+int rmdir(const char *path) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd___wasilibc_rmdirat(dirfd, relative_path);
+}
+
+int remove(const char *path) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ // First try to remove it as a file.
+ int r = __wasilibc_nocwd___wasilibc_unlinkat(dirfd, relative_path);
+ if (r != 0 && (errno == EISDIR || errno == ENOTCAPABLE)) {
+ // That failed, but it might be a directory.
+ r = __wasilibc_nocwd___wasilibc_rmdirat(dirfd, relative_path);
+
+ // If it isn't a directory, we lack capabilities to remove it as a file.
+ if (errno == ENOTDIR)
+ errno = ENOTCAPABLE;
+ }
+ return r;
+}
+
+int mkdir(const char *path, mode_t mode) {
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_mkdirat_nomode(dirfd, relative_path);
+}
+
+DIR *opendir(const char *dirname) {
+ char *relative_path;
+ int dirfd = find_relpath(dirname, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return NULL;
+ }
+
+ return __wasilibc_nocwd_opendirat(dirfd, relative_path);
+}
+
+int scandir(
+ const char *restrict dir,
+ struct dirent ***restrict namelist,
+ int (*filter)(const struct dirent *),
+ int (*compar)(const struct dirent **, const struct dirent **)
+) {
+ char *relative_path;
+ int dirfd = find_relpath(dir, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_scandirat(dirfd, relative_path, namelist, filter, compar);
+}
+
+int symlink(const char *target, const char *linkpath) {
+ char *relative_path;
+ int dirfd = find_relpath(linkpath, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_symlinkat(target, dirfd, relative_path);
+}
+
+int link(const char *old, const char *new) {
+ char *old_relative_path;
+ int old_dirfd = find_relpath_alt(old, &old_relative_path);
+
+ if (old_dirfd != -1) {
+ char *new_relative_path;
+ int new_dirfd = find_relpath(new, &new_relative_path);
+
+ if (new_dirfd != -1)
+ return __wasilibc_nocwd_linkat(old_dirfd, old_relative_path,
+ new_dirfd, new_relative_path, 0);
+ }
+
+ // We couldn't find a preopen for it; indicate that we lack capabilities.
+ errno = ENOTCAPABLE;
+ return -1;
+}
+
+int rename(const char *old, const char *new) {
+ char *old_relative_path;
+ int old_dirfd = find_relpath_alt(old, &old_relative_path);
+
+ if (old_dirfd != -1) {
+ char *new_relative_path;
+ int new_dirfd = find_relpath(new, &new_relative_path);
+
+ if (new_dirfd != -1)
+ return __wasilibc_nocwd_renameat(old_dirfd, old_relative_path,
+ new_dirfd, new_relative_path);
+ }
+
+ // We couldn't find a preopen for it; indicate that we lack capabilities.
+ errno = ENOTCAPABLE;
+ return -1;
+}
+
+// Like `access`, but with `faccessat`'s flags argument.
+int
+__wasilibc_access(const char *path, int mode, int flags)
+{
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_faccessat(dirfd, relative_path,
+ mode, flags);
+}
+
+// Like `utimensat`, but without the `at` part.
+int
+__wasilibc_utimens(const char *path, const struct timespec times[2], int flags)
+{
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_utimensat(dirfd, relative_path,
+ times, flags);
+}
+
+// Like `stat`, but with `fstatat`'s flags argument.
+int
+__wasilibc_stat(const char *__restrict path, struct stat *__restrict st, int flags)
+{
+ char *relative_path;
+ int dirfd = find_relpath(path, &relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_fstatat(dirfd, relative_path, st, flags);
+}
+
+// Like `link`, but with `linkat`'s flags argument.
+int
+__wasilibc_link(const char *oldpath, const char *newpath, int flags)
+{
+ char *old_relative_path;
+ char *new_relative_path;
+ int old_dirfd = find_relpath(oldpath, &old_relative_path);
+ int new_dirfd = find_relpath(newpath, &new_relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (old_dirfd == -1 || new_dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_linkat(old_dirfd, old_relative_path,
+ new_dirfd, new_relative_path,
+ flags);
+}
+
+// Like `__wasilibc_link`, but oldpath is relative to olddirfd.
+int
+__wasilibc_link_oldat(int olddirfd, const char *oldpath, const char *newpath, int flags)
+{
+ char *new_relative_path;
+ int new_dirfd = find_relpath(newpath, &new_relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (new_dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_linkat(olddirfd, oldpath,
+ new_dirfd, new_relative_path,
+ flags);
+}
+
+// Like `__wasilibc_link`, but newpath is relative to newdirfd.
+int
+__wasilibc_link_newat(const char *oldpath, int newdirfd, const char *newpath, int flags)
+{
+ char *old_relative_path;
+ int old_dirfd = find_relpath(oldpath, &old_relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (old_dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_linkat(old_dirfd, old_relative_path,
+ newdirfd, newpath,
+ flags);
+}
+
+// Like `rename`, but from is relative to fromdirfd.
+int
+__wasilibc_rename_oldat(int fromdirfd, const char *from, const char *to)
+{
+ char *to_relative_path;
+ int to_dirfd = find_relpath(to, &to_relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (to_dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_renameat(fromdirfd, from, to_dirfd, to_relative_path);
+}
+
+// Like `rename`, but to is relative to todirfd.
+int
+__wasilibc_rename_newat(const char *from, int todirfd, const char *to)
+{
+ char *from_relative_path;
+ int from_dirfd = find_relpath(from, &from_relative_path);
+
+ // If we can't find a preopen for it, indicate that we lack capabilities.
+ if (from_dirfd == -1) {
+ errno = ENOTCAPABLE;
+ return -1;
+ }
+
+ return __wasilibc_nocwd_renameat(from_dirfd, from_relative_path, todirfd, to);
+}
diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c
new file mode 100644
index 0000000..7293c8c
--- /dev/null
+++ b/libc-bottom-half/sources/preopens.c
@@ -0,0 +1,268 @@
+//! Support for "preopens", file descriptors passed into the program from the
+//! environment, with associated path prefixes, which can be used to map
+//! absolute paths to capabilities with relative paths.
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <lock.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <wasi/api.h>
+#include <wasi/libc-find-relpath.h>
+#include <wasi/libc.h>
+
+/// A name and file descriptor pair.
+typedef struct preopen {
+ /// The path prefix associated with the file descriptor.
+ const char *prefix;
+
+ /// The file descriptor.
+ __wasi_fd_t fd;
+} preopen;
+
+/// A simple growable array of `preopen`.
+static preopen *preopens;
+static size_t num_preopens;
+static size_t preopen_capacity;
+
+/// Access to the the above preopen must be protected in the presence of
+/// threads.
+#ifdef _REENTRANT
+static volatile int lock[1];
+#endif
+
+#ifdef NDEBUG
+#define assert_invariants() // assertions disabled
+#else
+static void assert_invariants(void) {
+ assert(num_preopens <= preopen_capacity);
+ assert(preopen_capacity == 0 || preopens != NULL);
+ assert(preopen_capacity == 0 ||
+ preopen_capacity * sizeof(preopen) > preopen_capacity);
+
+ for (size_t i = 0; i < num_preopens; ++i) {
+ const preopen *pre = &preopens[i];
+ assert(pre->prefix != NULL);
+ assert(pre->fd != (__wasi_fd_t)-1);
+#ifdef __wasm__
+ assert((uintptr_t)pre->prefix <
+ (__uint128_t)__builtin_wasm_memory_size(0) * PAGESIZE);
+#endif
+ }
+}
+#endif
+
+/// Allocate space for more preopens. Returns 0 on success and -1 on failure.
+static int resize(void) {
+ LOCK(lock);
+ size_t start_capacity = 4;
+ size_t old_capacity = preopen_capacity;
+ size_t new_capacity = old_capacity == 0 ? start_capacity : old_capacity * 2;
+
+ preopen *old_preopens = preopens;
+ preopen *new_preopens = calloc(sizeof(preopen), new_capacity);
+ if (new_preopens == NULL) {
+ UNLOCK(lock);
+ return -1;
+ }
+
+ memcpy(new_preopens, old_preopens, num_preopens * sizeof(preopen));
+ preopens = new_preopens;
+ preopen_capacity = new_capacity;
+ free(old_preopens);
+
+ assert_invariants();
+ UNLOCK(lock);
+ return 0;
+}
+
+// Normalize an absolute path. Removes leading `/` and leading `./`, so the
+// first character is the start of a directory name. This works because our
+// process always starts with a working directory of `/`. Additionally translate
+// `.` to the empty string.
+static const char *strip_prefixes(const char *path) {
+ while (1) {
+ if (path[0] == '/') {
+ path++;
+ } else if (path[0] == '.' && path[1] == '/') {
+ path += 2;
+ } else if (path[0] == '.' && path[1] == 0) {
+ path++;
+ } else {
+ break;
+ }
+ }
+
+ return path;
+}
+
+/// Register the given preopened file descriptor under the given path.
+///
+/// This function takes ownership of `prefix`.
+static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) {
+ LOCK(lock);
+
+ // Check preconditions.
+ assert_invariants();
+ assert(fd != AT_FDCWD);
+ assert(fd != -1);
+ assert(relprefix != NULL);
+
+ if (num_preopens == preopen_capacity && resize() != 0) {
+ UNLOCK(lock);
+ return -1;
+ }
+
+ char *prefix = strdup(strip_prefixes(relprefix));
+ if (prefix == NULL) {
+ UNLOCK(lock);
+ return -1;
+ }
+ preopens[num_preopens++] = (preopen) { prefix, fd, };
+
+ assert_invariants();
+ UNLOCK(lock);
+ return 0;
+}
+
+/// Are the `prefix_len` bytes pointed to by `prefix` a prefix of `path`?
+static bool prefix_matches(const char *prefix, size_t prefix_len, const char *path) {
+ // Allow an empty string as a prefix of any relative path.
+ if (path[0] != '/' && prefix_len == 0)
+ return true;
+
+ // Check whether any bytes of the prefix differ.
+ if (memcmp(path, prefix, prefix_len) != 0)
+ return false;
+
+ // Ignore trailing slashes in directory names.
+ size_t i = prefix_len;
+ while (i > 0 && prefix[i - 1] == '/') {
+ --i;
+ }
+
+ // Match only complete path components.
+ char last = path[i];
+ return last == '/' || last == '\0';
+}
+
+// See the documentation in libc.h
+int __wasilibc_register_preopened_fd(int fd, const char *prefix) {
+ return internal_register_preopened_fd((__wasi_fd_t)fd, prefix);
+}
+
+// See the documentation in libc-find-relpath.h.
+int __wasilibc_find_relpath(const char *path,
+ const char **abs_prefix,
+ char **relative_path,
+ size_t relative_path_len) {
+ // If `chdir` is linked, whose object file defines this symbol, then we
+ // call that. Otherwise if the program can't `chdir` then `path` is
+ // absolute (or relative to the root dir), so we delegate to `find_abspath`
+ if (__wasilibc_find_relpath_alloc)
+ return __wasilibc_find_relpath_alloc(path, abs_prefix, relative_path, &relative_path_len, 0);
+ return __wasilibc_find_abspath(path, abs_prefix, (const char**) relative_path);
+}
+
+// See the documentation in libc-find-relpath.h.
+int __wasilibc_find_abspath(const char *path,
+ const char **abs_prefix,
+ const char **relative_path) {
+ // Strip leading `/` characters, the prefixes we're mataching won't have
+ // them.
+ while (*path == '/')
+ path++;
+ // Search through the preopens table. Iterate in reverse so that more
+ // recently added preopens take precedence over less recently addded ones.
+ size_t match_len = 0;
+ int fd = -1;
+ LOCK(lock);
+ for (size_t i = num_preopens; i > 0; --i) {
+ const preopen *pre = &preopens[i - 1];
+ const char *prefix = pre->prefix;
+ size_t len = strlen(prefix);
+
+ // If we haven't had a match yet, or the candidate path is longer than
+ // our current best match's path, and the candidate path is a prefix of
+ // the requested path, take that as the new best path.
+ if ((fd == -1 || len > match_len) &&
+ prefix_matches(prefix, len, path))
+ {
+ fd = pre->fd;
+ match_len = len;
+ *abs_prefix = prefix;
+ }
+ }
+ UNLOCK(lock);
+
+ if (fd == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ // The relative path is the substring after the portion that was matched.
+ const char *computed = path + match_len;
+
+ // Omit leading slashes in the relative path.
+ while (*computed == '/')
+ ++computed;
+
+ // *at syscalls don't accept empty relative paths, so use "." instead.
+ if (*computed == '\0')
+ computed = ".";
+
+ *relative_path = computed;
+ return fd;
+}
+
+/// This is referenced by weak reference from crt1.c and lives in the same
+/// source file as `__wasilibc_find_relpath` so that it's linked in when it's
+/// needed.
+// Concerning the 51 -- see the comment by the constructor priority in
+// libc-bottom-half/sources/environ.c.
+__attribute__((constructor(51)))
+static void __wasilibc_populate_preopens(void) {
+ // Skip stdin, stdout, and stderr, and count up until we reach an invalid
+ // file descriptor.
+ for (__wasi_fd_t fd = 3; fd != 0; ++fd) {
+ __wasi_prestat_t prestat;
+ __wasi_errno_t ret = __wasi_fd_prestat_get(fd, &prestat);
+ if (ret == __WASI_ERRNO_BADF)
+ break;
+ if (ret != __WASI_ERRNO_SUCCESS)
+ goto oserr;
+ switch (prestat.tag) {
+ case __WASI_PREOPENTYPE_DIR: {
+ char *prefix = malloc(prestat.u.dir.pr_name_len + 1);
+ if (prefix == NULL)
+ goto software;
+
+ // TODO: Remove the cast on `path` once the witx is updated with
+ // char8 support.
+ ret = __wasi_fd_prestat_dir_name(fd, (uint8_t *)prefix,
+ prestat.u.dir.pr_name_len);
+ if (ret != __WASI_ERRNO_SUCCESS)
+ goto oserr;
+ prefix[prestat.u.dir.pr_name_len] = '\0';
+
+ if (internal_register_preopened_fd(fd, prefix) != 0)
+ goto software;
+ free(prefix);
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return;
+oserr:
+ _Exit(EX_OSERR);
+software:
+ _Exit(EX_SOFTWARE);
+}
diff --git a/libc-bottom-half/sources/reallocarray.c b/libc-bottom-half/sources/reallocarray.c
new file mode 100644
index 0000000..3e828cc
--- /dev/null
+++ b/libc-bottom-half/sources/reallocarray.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <errno.h>
+
+void *__reallocarray(void *ptr, size_t nmemb, size_t size) {
+ size_t bytes;
+ if (__builtin_umull_overflow(nmemb, size, &bytes)) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(ptr, bytes);
+}
+
+void *reallocarray(void *ptr, size_t nmemb, size_t size)
+ __attribute__((__weak__, __alias__("__reallocarray")));
diff --git a/libc-bottom-half/sources/sbrk.c b/libc-bottom-half/sources/sbrk.c
new file mode 100644
index 0000000..a26b75e
--- /dev/null
+++ b/libc-bottom-half/sources/sbrk.c
@@ -0,0 +1,32 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <__macro_PAGESIZE.h>
+
+// Bare-bones implementation of sbrk.
+void *sbrk(intptr_t increment) {
+ // sbrk(0) returns the current memory size.
+ if (increment == 0) {
+ // The wasm spec doesn't guarantee that memory.grow of 0 always succeeds.
+ return (void *)(__builtin_wasm_memory_size(0) * PAGESIZE);
+ }
+
+ // We only support page-size increments.
+ if (increment % PAGESIZE != 0) {
+ abort();
+ }
+
+ // WebAssembly doesn't support shrinking linear memory.
+ if (increment < 0) {
+ abort();
+ }
+
+ uintptr_t old = __builtin_wasm_memory_grow(0, (uintptr_t)increment / PAGESIZE);
+
+ if (old == SIZE_MAX) {
+ errno = ENOMEM;
+ return (void *)-1;
+ }
+
+ return (void *)(old * PAGESIZE);
+}
diff --git a/libc-bottom-half/sources/truncate.c b/libc-bottom-half/sources/truncate.c
new file mode 100644
index 0000000..24ae28f
--- /dev/null
+++ b/libc-bottom-half/sources/truncate.c
@@ -0,0 +1,21 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <wasi/libc.h>
+
+int truncate(const char *path, off_t length)
+{
+ int fd = __wasilibc_open_nomode(path, O_WRONLY | O_CLOEXEC | O_NOCTTY);
+ if (fd < 0)
+ return -1;
+
+ int result = ftruncate(fd, length);
+ if (result != 0) {
+ int save_errno = errno;
+ (void)close(fd);
+ errno = save_errno;
+ return -1;
+ }
+
+ return close(fd);
+}