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