summaryrefslogtreecommitdiffstats
path: root/libc-bottom-half/sources
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/sources
parentInitial commit. (diff)
downloadwasi-libc-upstream/0.0_git20221206.8b7148f.tar.xz
wasi-libc-upstream/0.0_git20221206.8b7148f.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/sources')
-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
27 files changed, 2325 insertions, 0 deletions
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);
+}