diff options
Diffstat (limited to 'libc-bottom-half/sources')
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); +} |