mirror of
https://git.proxmox.com/git/wasi-libc
synced 2025-10-04 10:50:01 +00:00

* Fixes for the THREAD_MODEL=posix build * Fix expected symbols from previous commit * Enable `lock` in `random.c` when threads are enabled This uses the `_REENTRANT` definition to indicate when the `lock` should be available. * Disable `aio.h` when compiling for threads In talking to @sunfishcode about `aio.h`, this functionality is not yet a primary concern (it was already disabled in the default, single-threaded mode). Additionally, this change adds expectation lines for the new symbols/includes added and removed by `pthread.h`. This change was reached by running: ```console $ git diff --no-index expected/wasm32-wasi sysroot/share/wasm32-wasi > patch.diff # replace "sysroot/share" with "expected" in `patch.diff` $ git apply patch.diff --reject # manually fix any rejections ``` * Specify the TLS model until LLVM 15 is released The `-ftls-model` configuration can be removed once https://reviews.llvm.org/D130053 makes its way into an upstream release. * Rename `__wasi_libc_pthread_self` to `__wasilibc_pthread_self` The symbol is still undefined, though. * Add different sets of expected output based on THREAD_MODEL * Re-add trailing whitespace to `predefined-macros.txt` @sbc100 wanted to retain the whitespace trailing after certain predefined macro lines. This change restores that whitespace from upstream and re-generates the POSIX version using the following command: ```console $ git diff --no-index expected/wasm32-wasi/posix/predefined-macros.txt sysroot/share/wasm32-wasi/predefined-macros.txt | sed 's/sysroot\/share\/wasm32-wasi/expected\/wasm32-wasi\/posix/' | git apply ``` * Protect `preopens.c` against concurrent access * Only build thread-capable wasi-libc on latest version of Clang * Use `thrd_sleep` from MUSL instead of aliasing `nanosleep` * Define `pthread_setcancelstate` in `THREAD_MODEL=posix` builds There are other options here (e.g., always define the `pthread_*` symbols with stubs) but until we discuss that this is an intermediate working step. * Define a Wasm global to store `pthread_self` * Remove `g_needs_dynamic_alloc` global * Document the state of pthread support * review: de-duplicate symbols based on #314 * review: only define `__wasilibc_cwd_{un}lock` when needed * review: add #ifdefs to `__pthread_setcancelstate` * review: add additional #ifdefs to `pthread_self.c` * review: put lock definition behind #ifdef _REENTRANT * review: remove pthread_setcancelstate.c * review: re-fix indentation * review: alias __clock_nanosleep in bottom half * review: remove extra line Co-authored-by: Sam Clegg <sbc@chromium.org>
266 lines
8.1 KiB
C
266 lines
8.1 KiB
C
//! 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);
|
|
}
|