diff options
-rw-r--r-- | Makefile | 56 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | dlmalloc/src/malloc.c | 4 | ||||
-rw-r--r-- | expected/wasm32-wasi-threads/defined-symbols.txt | 6 | ||||
-rw-r--r-- | expected/wasm32-wasi-threads/predefined-macros.txt | 5 | ||||
-rw-r--r-- | expected/wasm32-wasi/defined-symbols.txt | 1 | ||||
-rw-r--r-- | expected/wasm32-wasi/predefined-macros.txt | 5 | ||||
-rw-r--r-- | libc-bottom-half/cloudlibc/src/libc/unistd/close.c | 16 | ||||
-rw-r--r-- | libc-bottom-half/crt/crt1-reactor.c | 20 | ||||
-rw-r--r-- | libc-bottom-half/headers/public/__header_sys_socket.h | 2 | ||||
-rw-r--r-- | libc-bottom-half/headers/public/wasi/libc.h | 6 | ||||
-rw-r--r-- | libc-bottom-half/sources/__wasilibc_fd_renumber.c | 22 | ||||
-rw-r--r-- | libc-bottom-half/sources/__wasilibc_real.c | 2 | ||||
-rw-r--r-- | libc-bottom-half/sources/preopens.c | 58 | ||||
-rw-r--r-- | libc-top-half/musl/arch/wasm32/atomic_arch.h | 1 | ||||
-rw-r--r-- | libc-top-half/musl/include/pthread.h | 6 | ||||
-rw-r--r-- | libc-top-half/musl/src/env/__init_tls.c | 7 | ||||
-rw-r--r-- | libc-top-half/musl/src/thread/pthread_create.c | 53 | ||||
-rw-r--r-- | libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s | 17 |
19 files changed, 198 insertions, 96 deletions
@@ -20,7 +20,7 @@ MALLOC_IMPL ?= dlmalloc # yes or no BUILD_LIBC_TOP_HALF ?= yes # The directory where we will store intermediate artifacts. -OBJDIR ?= $(CURDIR)/build/$(TARGET_TRIPLE) +OBJDIR ?= build/$(TARGET_TRIPLE) # When the length is no larger than this threshold, we consider the # overhead of bulk memory opcodes to outweigh the performance benefit, @@ -43,13 +43,13 @@ endif # These variables describe the locations of various files and directories in # the source tree. -DLMALLOC_DIR = $(CURDIR)/dlmalloc +DLMALLOC_DIR = dlmalloc DLMALLOC_SRC_DIR = $(DLMALLOC_DIR)/src DLMALLOC_SOURCES = $(DLMALLOC_SRC_DIR)/dlmalloc.c DLMALLOC_INC = $(DLMALLOC_DIR)/include -EMMALLOC_DIR = $(CURDIR)/emmalloc +EMMALLOC_DIR = emmalloc EMMALLOC_SOURCES = $(EMMALLOC_DIR)/emmalloc.c -LIBC_BOTTOM_HALF_DIR = $(CURDIR)/libc-bottom-half +LIBC_BOTTOM_HALF_DIR = libc-bottom-half LIBC_BOTTOM_HALF_CLOUDLIBC_SRC = $(LIBC_BOTTOM_HALF_DIR)/cloudlibc/src LIBC_BOTTOM_HALF_CLOUDLIBC_SRC_INC = $(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC)/include LIBC_BOTTOM_HALF_HEADERS_PUBLIC = $(LIBC_BOTTOM_HALF_DIR)/headers/public @@ -79,7 +79,7 @@ LIBWASI_EMULATED_SIGNAL_MUSL_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/signal/psignal.c \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/string/strsignal.c LIBC_BOTTOM_HALF_CRT_SOURCES = $(wildcard $(LIBC_BOTTOM_HALF_DIR)/crt/*.c) -LIBC_TOP_HALF_DIR = $(CURDIR)/libc-top-half +LIBC_TOP_HALF_DIR = libc-top-half LIBC_TOP_HALF_MUSL_DIR = $(LIBC_TOP_HALF_DIR)/musl LIBC_TOP_HALF_MUSL_SRC_DIR = $(LIBC_TOP_HALF_MUSL_DIR)/src LIBC_TOP_HALF_MUSL_INC = $(LIBC_TOP_HALF_MUSL_DIR)/include @@ -262,6 +262,11 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ thread/pthread_setcancelstate.c \ thread/pthread_setspecific.c \ thread/pthread_self.c \ + thread/pthread_spin_destroy.c \ + thread/pthread_spin_init.c \ + thread/pthread_spin_lock.c \ + thread/pthread_spin_trylock.c \ + thread/pthread_spin_unlock.c \ thread/pthread_testcancel.c \ thread/sem_destroy.c \ thread/sem_getvalue.c \ @@ -320,6 +325,7 @@ ifeq ($(THREAD_MODEL), posix) # Specify the tls-model until LLVM 15 is released (which should contain # https://reviews.llvm.org/D130053). CFLAGS += -mthread-model posix -pthread -ftls-model=local-exec +ASMFLAGS += -matomics # Include cloudlib's directory to access the structure definition of clockid_t CFLAGS += -I$(LIBC_BOTTOM_HALF_CLOUDLIBC_SRC) @@ -341,8 +347,8 @@ CFLAGS += -isystem "$(SYSROOT_INC)" # These variables describe the locations of various files and directories in # the build tree. -objs = $(patsubst $(CURDIR)/%.c,$(OBJDIR)/%.o,$(1)) -asmobjs = $(patsubst $(CURDIR)/%.s,$(OBJDIR)/%.o,$(1)) +objs = $(patsubst %.c,$(OBJDIR)/%.o,$(1)) +asmobjs = $(patsubst %.s,$(OBJDIR)/%.o,$(1)) DLMALLOC_OBJS = $(call objs,$(DLMALLOC_SOURCES)) EMMALLOC_OBJS = $(call objs,$(EMMALLOC_SOURCES)) LIBC_BOTTOM_HALF_ALL_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_ALL_SOURCES)) @@ -371,6 +377,7 @@ LIBWASI_EMULATED_PROCESS_CLOCKS_OBJS = $(call objs,$(LIBWASI_EMULATED_PROCESS_CL LIBWASI_EMULATED_GETPID_OBJS = $(call objs,$(LIBWASI_EMULATED_GETPID_SOURCES)) LIBWASI_EMULATED_SIGNAL_OBJS = $(call objs,$(LIBWASI_EMULATED_SIGNAL_SOURCES)) LIBWASI_EMULATED_SIGNAL_MUSL_OBJS = $(call objs,$(LIBWASI_EMULATED_SIGNAL_MUSL_SOURCES)) +LIBC_BOTTOM_HALF_CRT_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_CRT_SOURCES)) # These variables describe the locations of various files and # directories in the generated sysroot tree. @@ -482,13 +489,13 @@ $(SYSROOT_LIB)/libwasi-emulated-signal.a: $(LIBWASI_EMULATED_SIGNAL_OBJS) $(LIBW %.a: @mkdir -p "$(@D)" # On Windows, the commandline for the ar invocation got too long, so it needs to be split up. - $(AR) crs $@ $(wordlist 1, 199, $^) - $(AR) crs $@ $(wordlist 200, 399, $^) - $(AR) crs $@ $(wordlist 400, 599, $^) - $(AR) crs $@ $(wordlist 600, 799, $^) + $(AR) crs $@ $(wordlist 1, 199, $(sort $^)) + $(AR) crs $@ $(wordlist 200, 399, $(sort $^)) + $(AR) crs $@ $(wordlist 400, 599, $(sort $^)) + $(AR) crs $@ $(wordlist 600, 799, $(sort $^)) # This might eventually overflow again, but at least it'll do so in a loud way instead of # silently dropping the tail. - $(AR) crs $@ $(wordlist 800, 100000, $^) + $(AR) crs $@ $(wordlist 800, 100000, $(sort $^)) $(MUSL_PRINTSCAN_OBJS): CFLAGS += \ -D__wasilibc_printscan_no_long_double \ @@ -509,19 +516,19 @@ $(BULK_MEMORY_OBJS): CFLAGS += \ $(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS): CFLAGS += \ -D_WASI_EMULATED_SIGNAL -$(OBJDIR)/%.long-double.o: $(CURDIR)/%.c include_dirs +$(OBJDIR)/%.long-double.o: %.c include_dirs @mkdir -p "$(@D)" $(CC) $(CFLAGS) -MD -MP -o $@ -c $< -$(OBJDIR)/%.no-floating-point.o: $(CURDIR)/%.c include_dirs +$(OBJDIR)/%.no-floating-point.o: %.c include_dirs @mkdir -p "$(@D)" $(CC) $(CFLAGS) -MD -MP -o $@ -c $< -$(OBJDIR)/%.o: $(CURDIR)/%.c include_dirs +$(OBJDIR)/%.o: %.c include_dirs @mkdir -p "$(@D)" $(CC) $(CFLAGS) -MD -MP -o $@ -c $< -$(OBJDIR)/%.o: $(CURDIR)/%.s include_dirs +$(OBJDIR)/%.o: %.s include_dirs @mkdir -p "$(@D)" $(CC) $(ASMFLAGS) -o $@ -c $< @@ -577,15 +584,12 @@ include_dirs: # Remove selected header files. $(RM) $(patsubst %,$(SYSROOT_INC)/%,$(MUSL_OMIT_HEADERS)) -startup_files: include_dirs +startup_files: include_dirs $(LIBC_BOTTOM_HALF_CRT_OBJS) # - # Build the startup files. + # Install the startup files (crt1.o etc). # - @mkdir -p "$(OBJDIR)" - cd "$(OBJDIR)" && \ - $(CC) $(CFLAGS) -c $(LIBC_BOTTOM_HALF_CRT_SOURCES) -MD -MP && \ mkdir -p "$(SYSROOT_LIB)" && \ - mv *.o "$(SYSROOT_LIB)" + cp $(LIBC_BOTTOM_HALF_CRT_OBJS) "$(SYSROOT_LIB)" libc: include_dirs \ $(SYSROOT_LIB)/libc.a \ @@ -668,6 +672,7 @@ check-symbols: startup_files libc @# @# TODO: Filter out __NO_MATH_ERRNO_ and a few __*WIDTH__ that are new to clang 14. @# TODO: Filter out __GCC_HAVE_SYNC_COMPARE_AND_SWAP_* that are new to clang 16. + @# TODO: Filter out __FPCLASS_* that are new to clang 17. @# TODO: clang defined __FLT_EVAL_METHOD__ until clang 15, so we force-undefine it @# for older versions. @# TODO: Undefine __wasm_mutable_globals__ and __wasm_sign_ext__, that are new to @@ -699,11 +704,16 @@ check-symbols: startup_files libc | sed -e 's/__GNUC_VA_LIST $$/__GNUC_VA_LIST 1/' \ | grep -v '^#define __\(BOOL\|INT_\(LEAST\|FAST\)\(8\|16\|32\|64\)\|INT\|LONG\|LLONG\|SHRT\)_WIDTH__' \ | grep -v '^#define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_\(1\|2\|4\|8\)' \ + | grep -v '^#define __FPCLASS_' \ + | grep -v '^#define NDEBUG' \ + | grep -v '^#define __OPTIMIZE__' \ + | grep -v '^#define assert' \ + | grep -v '^#define __NO_INLINE__' \ > "$(SYSROOT_SHARE)/predefined-macros.txt" # Check that the computed metadata matches the expected metadata. # This ignores whitespace because on Windows the output has CRLF line endings. - diff -wur "$(CURDIR)/expected/$(TARGET_TRIPLE)" "$(SYSROOT_SHARE)" + diff -wur "expected/$(TARGET_TRIPLE)" "$(SYSROOT_SHARE)" install: finish mkdir -p "$(INSTALL_DIR)" @@ -45,7 +45,10 @@ extra setup. This is one of the things [wasi-sdk] simplifies, as it includes cross-compiled builds of compiler-rt, libc++.a, and libc++abi.a. ## Arch Linux AUR package -For Arch Linux users, there's an unofficial AUR package tracking this git repo that can be installed under the name [wasi-libc-git]. + +For Arch Linux users, there's an official [wasi-libc] package tracking this Git +repository. You might want to install other [WASI related packages] as well. [wasi-sdk]: https://github.com/WebAssembly/wasi-sdk -[wasi-libc-git]: https://aur.archlinux.org/packages/wasi-libc-git/ +[wasi-libc]: https://archlinux.org/packages/community/any/wasi-libc/ +[WASI related packages]: https://archlinux.org/packages/?q=wasi- diff --git a/dlmalloc/src/malloc.c b/dlmalloc/src/malloc.c index 6e7a15c..c9c8ea6 100644 --- a/dlmalloc/src/malloc.c +++ b/dlmalloc/src/malloc.c @@ -2839,7 +2839,7 @@ static size_t traverse_and_check(mstate m); #define treebin_at(M,i) (&((M)->treebins[i])) /* assign tree index for size S to variable I. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__) || defined(__wasm__)) #define compute_tree_index(S, I)\ {\ unsigned int X = S >> TREEBIN_SHIFT;\ @@ -2942,7 +2942,7 @@ static size_t traverse_and_check(mstate m); /* index corresponding to given bit. Use x86 asm if possible */ -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__) || defined(__wasm__)) #define compute_bit2idx(X, I)\ {\ unsigned int J;\ diff --git a/expected/wasm32-wasi-threads/defined-symbols.txt b/expected/wasm32-wasi-threads/defined-symbols.txt index 8dba0f1..c913f16 100644 --- a/expected/wasm32-wasi-threads/defined-symbols.txt +++ b/expected/wasm32-wasi-threads/defined-symbols.txt @@ -390,6 +390,7 @@ __wasilibc_nocwd_scandirat __wasilibc_nocwd_symlinkat __wasilibc_nocwd_utimensat __wasilibc_open_nomode +__wasilibc_populate_preopens __wasilibc_pthread_self __wasilibc_register_preopened_fd __wasilibc_rename_newat @@ -1044,6 +1045,11 @@ pthread_rwlockattr_setpshared pthread_self pthread_setcancelstate pthread_setspecific +pthread_spin_destroy +pthread_spin_init +pthread_spin_lock +pthread_spin_trylock +pthread_spin_unlock pthread_testcancel pthread_timedjoin_np pthread_tryjoin_np diff --git a/expected/wasm32-wasi-threads/predefined-macros.txt b/expected/wasm32-wasi-threads/predefined-macros.txt index a9f2982..0fca3e4 100644 --- a/expected/wasm32-wasi-threads/predefined-macros.txt +++ b/expected/wasm32-wasi-threads/predefined-macros.txt @@ -1098,7 +1098,7 @@ #define MSG_BAND 0x04 #define MSG_HIPRI 0x01 #define MSG_PEEK __WASI_RIFLAGS_RECV_PEEK -#define MSG_TRUNC __WASI_RIFLAGS_RECV_DATA_TRUNCATED +#define MSG_TRUNC __WASI_ROFLAGS_RECV_DATA_TRUNCATED #define MSG_WAITALL __WASI_RIFLAGS_RECV_WAITALL #define MUXID_ALL (-1) #define M_1_PI 0.31830988618379067154 @@ -1119,7 +1119,6 @@ #define NAN (0.0f/0.0f) #define NBBY 8 #define NCARGS 131072 -#define NDEBUG 1 #define ND_NA_FLAG_OVERRIDE 0x00000020 #define ND_NA_FLAG_ROUTER 0x00000080 #define ND_NA_FLAG_SOLICITED 0x00000040 @@ -2685,7 +2684,6 @@ #define __OPENCL_MEMORY_SCOPE_SUB_GROUP 4 #define __OPENCL_MEMORY_SCOPE_WORK_GROUP 1 #define __OPENCL_MEMORY_SCOPE_WORK_ITEM 0 -#define __OPTIMIZE__ 1 #define __ORDER_BIG_ENDIAN__ 4321 #define __ORDER_LITTLE_ENDIAN__ 1234 #define __ORDER_PDP_ENDIAN__ 3412 @@ -3112,7 +3110,6 @@ #define and_eq &= #define asin(x) __tg_real_complex(asin, (x)) #define asinh(x) __tg_real_complex(asinh, (x)) -#define assert(x) (void)0 #define atan(x) __tg_real_complex(atan, (x)) #define atan2(x,y) __tg_real_2(atan2, (x), (y)) #define atanh(x) __tg_real_complex(atanh, (x)) diff --git a/expected/wasm32-wasi/defined-symbols.txt b/expected/wasm32-wasi/defined-symbols.txt index a692632..6e74322 100644 --- a/expected/wasm32-wasi/defined-symbols.txt +++ b/expected/wasm32-wasi/defined-symbols.txt @@ -329,6 +329,7 @@ __wasilibc_nocwd_scandirat __wasilibc_nocwd_symlinkat __wasilibc_nocwd_utimensat __wasilibc_open_nomode +__wasilibc_populate_preopens __wasilibc_register_preopened_fd __wasilibc_rename_newat __wasilibc_rename_oldat diff --git a/expected/wasm32-wasi/predefined-macros.txt b/expected/wasm32-wasi/predefined-macros.txt index 234ad3c..d6cae22 100644 --- a/expected/wasm32-wasi/predefined-macros.txt +++ b/expected/wasm32-wasi/predefined-macros.txt @@ -1098,7 +1098,7 @@ #define MSG_BAND 0x04 #define MSG_HIPRI 0x01 #define MSG_PEEK __WASI_RIFLAGS_RECV_PEEK -#define MSG_TRUNC __WASI_RIFLAGS_RECV_DATA_TRUNCATED +#define MSG_TRUNC __WASI_ROFLAGS_RECV_DATA_TRUNCATED #define MSG_WAITALL __WASI_RIFLAGS_RECV_WAITALL #define MUXID_ALL (-1) #define M_1_PI 0.31830988618379067154 @@ -1119,7 +1119,6 @@ #define NAN (0.0f/0.0f) #define NBBY 8 #define NCARGS 131072 -#define NDEBUG 1 #define ND_NA_FLAG_OVERRIDE 0x00000020 #define ND_NA_FLAG_ROUTER 0x00000080 #define ND_NA_FLAG_SOLICITED 0x00000040 @@ -2648,7 +2647,6 @@ #define __OPENCL_MEMORY_SCOPE_SUB_GROUP 4 #define __OPENCL_MEMORY_SCOPE_WORK_GROUP 1 #define __OPENCL_MEMORY_SCOPE_WORK_ITEM 0 -#define __OPTIMIZE__ 1 #define __ORDER_BIG_ENDIAN__ 4321 #define __ORDER_LITTLE_ENDIAN__ 1234 #define __ORDER_PDP_ENDIAN__ 3412 @@ -3073,7 +3071,6 @@ #define and_eq &= #define asin(x) __tg_real_complex(asin, (x)) #define asinh(x) __tg_real_complex(asinh, (x)) -#define assert(x) (void)0 #define atan(x) __tg_real_complex(atan, (x)) #define atan2(x,y) __tg_real_2(atan2, (x), (y)) #define atanh(x) __tg_real_complex(atanh, (x)) diff --git a/libc-bottom-half/cloudlibc/src/libc/unistd/close.c b/libc-bottom-half/cloudlibc/src/libc/unistd/close.c deleted file mode 100644 index 2f5814b..0000000 --- a/libc-bottom-half/cloudlibc/src/libc/unistd/close.c +++ /dev/null @@ -1,16 +0,0 @@ -// 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/crt/crt1-reactor.c b/libc-bottom-half/crt/crt1-reactor.c index f507c9e..ea4a84f 100644 --- a/libc-bottom-half/crt/crt1-reactor.c +++ b/libc-bottom-half/crt/crt1-reactor.c @@ -1,7 +1,27 @@ +#if defined(_REENTRANT) +#include <stdatomic.h> +extern void __wasi_init_tp(void); +#endif extern void __wasm_call_ctors(void); __attribute__((export_name("_initialize"))) void _initialize(void) { +#if defined(_REENTRANT) + static volatile atomic_int initialized = 0; + int expected = 0; + if (!atomic_compare_exchange_strong(&initialized, &expected, 1)) { + __builtin_trap(); + } + + __wasi_init_tp(); +#else + static volatile int initialized = 0; + if (initialized != 0) { + __builtin_trap(); + } + initialized = 1; +#endif + // The linker synthesizes this to call constructors. __wasm_call_ctors(); } diff --git a/libc-bottom-half/headers/public/__header_sys_socket.h b/libc-bottom-half/headers/public/__header_sys_socket.h index 9fa8684..77aaa1b 100644 --- a/libc-bottom-half/headers/public/__header_sys_socket.h +++ b/libc-bottom-half/headers/public/__header_sys_socket.h @@ -13,7 +13,7 @@ #define MSG_PEEK __WASI_RIFLAGS_RECV_PEEK #define MSG_WAITALL __WASI_RIFLAGS_RECV_WAITALL -#define MSG_TRUNC __WASI_RIFLAGS_RECV_DATA_TRUNCATED +#define MSG_TRUNC __WASI_ROFLAGS_RECV_DATA_TRUNCATED #define SOCK_DGRAM __WASI_FILETYPE_SOCKET_DGRAM #define SOCK_STREAM __WASI_FILETYPE_SOCKET_STREAM diff --git a/libc-bottom-half/headers/public/wasi/libc.h b/libc-bottom-half/headers/public/wasi/libc.h index b50518b..18d8e9e 100644 --- a/libc-bottom-half/headers/public/wasi/libc.h +++ b/libc-bottom-half/headers/public/wasi/libc.h @@ -11,6 +11,12 @@ extern "C" { struct stat; struct timespec; +/// Populate libc's preopen tables. This is normally done automatically +/// just before it's needed, however if you call `__wasi_fd_renumber` or +/// `__wasi_fd_close` directly, and you need the preopens to be accurate +/// afterward, you should call this before doing so. +void __wasilibc_populate_preopens(void); + /// Register the given pre-opened file descriptor under the given path. /// /// This function does not take ownership of `prefix` (it makes its own copy). diff --git a/libc-bottom-half/sources/__wasilibc_fd_renumber.c b/libc-bottom-half/sources/__wasilibc_fd_renumber.c index aa9d8dc..47992e9 100644 --- a/libc-bottom-half/sources/__wasilibc_fd_renumber.c +++ b/libc-bottom-half/sources/__wasilibc_fd_renumber.c @@ -4,6 +4,9 @@ #include <unistd.h> int __wasilibc_fd_renumber(int fd, int newfd) { + // Scan the preopen fds before making any changes. + __wasilibc_populate_preopens(); + __wasi_errno_t error = __wasi_fd_renumber(fd, newfd); if (error != 0) { errno = error; @@ -11,3 +14,22 @@ int __wasilibc_fd_renumber(int fd, int newfd) { } return 0; } + +int close(int fd) { + // Scan the preopen fds before making any changes. + __wasilibc_populate_preopens(); + + __wasi_errno_t error = __wasi_fd_close(fd); + if (error != 0) { + errno = error; + return -1; + } + + return 0; +} + +weak void __wasilibc_populate_preopens(void) { + // This version does nothing. It may be overridden by a version which does + // something if `__wasilibc_find_abspath` or `__wasilibc_find_relpath` are + // used. +} diff --git a/libc-bottom-half/sources/__wasilibc_real.c b/libc-bottom-half/sources/__wasilibc_real.c index d2e6b71..186de01 100644 --- a/libc-bottom-half/sources/__wasilibc_real.c +++ b/libc-bottom-half/sources/__wasilibc_real.c @@ -662,7 +662,7 @@ __wasi_errno_t __wasi_sock_shutdown( #ifdef _REENTRANT int32_t __imported_wasi_thread_spawn(int32_t arg0) __attribute__(( __import_module__("wasi"), - __import_name__("thread_spawn") + __import_name__("thread-spawn") )); int32_t __wasi_thread_spawn(void* start_arg) { diff --git a/libc-bottom-half/sources/preopens.c b/libc-bottom-half/sources/preopens.c index 7293c8c..b495433 100644 --- a/libc-bottom-half/sources/preopens.c +++ b/libc-bottom-half/sources/preopens.c @@ -25,6 +25,7 @@ typedef struct preopen { } preopen; /// A simple growable array of `preopen`. +static _Atomic _Bool preopens_populated = false; static preopen *preopens; static size_t num_preopens; static size_t preopen_capacity; @@ -100,12 +101,9 @@ static const char *strip_prefixes(const char *path) { 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); - +/// Similar to `internal_register_preopened_fd_unlocked` but does not +/// take a lock. +static int internal_register_preopened_fd_unlocked(__wasi_fd_t fd, const char *relprefix) { // Check preconditions. assert_invariants(); assert(fd != AT_FDCWD); @@ -113,22 +111,32 @@ static int internal_register_preopened_fd(__wasi_fd_t fd, const char *relprefix) 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; } +/// 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); + + int r = internal_register_preopened_fd_unlocked(fd, relprefix); + + UNLOCK(lock); + + return r; +} + /// 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. @@ -152,6 +160,8 @@ static bool prefix_matches(const char *prefix, size_t prefix_len, const char *pa // See the documentation in libc.h int __wasilibc_register_preopened_fd(int fd, const char *prefix) { + __wasilibc_populate_preopens(); + return internal_register_preopened_fd((__wasi_fd_t)fd, prefix); } @@ -172,6 +182,8 @@ int __wasilibc_find_relpath(const char *path, int __wasilibc_find_abspath(const char *path, const char **abs_prefix, const char **relative_path) { + __wasilibc_populate_preopens(); + // Strip leading `/` characters, the prefixes we're mataching won't have // them. while (*path == '/') @@ -219,13 +231,20 @@ int __wasilibc_find_abspath(const char *path, 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) { +void __wasilibc_populate_preopens(void) { + // Fast path: If the preopens are already initialized, do nothing. + if (preopens_populated) { + return; + } + + LOCK(lock); + + // Check whether another thread initialized the preopens already. + if (preopens_populated) { + UNLOCK(lock); + return; + } + // Skip stdin, stdout, and stderr, and count up until we reach an invalid // file descriptor. for (__wasi_fd_t fd = 3; fd != 0; ++fd) { @@ -249,7 +268,7 @@ static void __wasilibc_populate_preopens(void) { goto oserr; prefix[prestat.u.dir.pr_name_len] = '\0'; - if (internal_register_preopened_fd(fd, prefix) != 0) + if (internal_register_preopened_fd_unlocked(fd, prefix) != 0) goto software; free(prefix); @@ -260,6 +279,11 @@ static void __wasilibc_populate_preopens(void) { } } + // Preopens are now initialized. + preopens_populated = true; + + UNLOCK(lock); + return; oserr: _Exit(EX_OSERR); diff --git a/libc-top-half/musl/arch/wasm32/atomic_arch.h b/libc-top-half/musl/arch/wasm32/atomic_arch.h index dd9428c..c24ce2d 100644 --- a/libc-top-half/musl/arch/wasm32/atomic_arch.h +++ b/libc-top-half/musl/arch/wasm32/atomic_arch.h @@ -1,4 +1,3 @@ -#define a_barrier() (__sync_synchronize()) #define a_cas(p, t, s) (__sync_val_compare_and_swap((p), (t), (s))) #define a_crash() (__builtin_trap()) #define a_clz_32 __builtin_clz diff --git a/libc-top-half/musl/include/pthread.h b/libc-top-half/musl/include/pthread.h index b14fe82..05101e8 100644 --- a/libc-top-half/musl/include/pthread.h +++ b/libc-top-half/musl/include/pthread.h @@ -55,15 +55,9 @@ extern "C" { #define PTHREAD_PROCESS_SHARED 1 -#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) #define PTHREAD_MUTEX_INITIALIZER {{{0}}} #define PTHREAD_RWLOCK_INITIALIZER {{{0}}} #define PTHREAD_COND_INITIALIZER {{{0}}} -#else -#define PTHREAD_MUTEX_INITIALIZER 0 -#define PTHREAD_RWLOCK_INITIALIZER 0 -#define PTHREAD_COND_INITIALIZER 0 -#endif #define PTHREAD_ONCE_INIT 0 diff --git a/libc-top-half/musl/src/env/__init_tls.c b/libc-top-half/musl/src/env/__init_tls.c index ece8d24..c3e407c 100644 --- a/libc-top-half/musl/src/env/__init_tls.c +++ b/libc-top-half/musl/src/env/__init_tls.c @@ -47,10 +47,9 @@ static inline void setup_default_stack_size() stack_size = sp > &__global_base ? &__heap_base - &__data_end : (ptrdiff_t)&__global_base; } - if (stack_size > __default_stacksize) - __default_stacksize = - stack_size < DEFAULT_STACK_MAX ? - stack_size : DEFAULT_STACK_MAX; + __default_stacksize = + stack_size < DEFAULT_STACK_MAX ? + stack_size : DEFAULT_STACK_MAX; } void __wasi_init_tp() { diff --git a/libc-top-half/musl/src/thread/pthread_create.c b/libc-top-half/musl/src/thread/pthread_create.c index 676e2cc..5de9f5a 100644 --- a/libc-top-half/musl/src/thread/pthread_create.c +++ b/libc-top-half/musl/src/thread/pthread_create.c @@ -60,6 +60,17 @@ void __tl_sync(pthread_t td) if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0); } +#ifndef __wasilibc_unmodified_upstream +static void *map_base_deferred_free; + +static void process_map_base_deferred_free() +{ + /* called with __tl_lock held */ + free(map_base_deferred_free); + map_base_deferred_free = NULL; +} +#endif + #ifdef __wasilibc_unmodified_upstream _Noreturn void __pthread_exit(void *result) #else @@ -164,14 +175,6 @@ static void __pthread_exit(void *result) self->prev->next = self->next; self->prev = self->next = self; -#ifndef __wasilibc_unmodified_upstream - /* On Linux, the thread is created with CLONE_CHILD_CLEARTID, - * and this lock will unlock by kernel when this thread terminates. - * So we should unlock it here in WebAssembly. - * See also set_tid_address(2) */ - __tl_unlock(); -#endif - #ifdef __wasilibc_unmodified_upstream if (state==DT_DETACHED && self->map_base) { /* Detached threads must block even implementation-internal @@ -190,10 +193,17 @@ static void __pthread_exit(void *result) } #else if (state==DT_DETACHED && self->map_base) { - // __syscall(SYS_exit) would unlock the thread, list - // do it manually here - __tl_unlock(); - free(self->map_base); + /* As we use malloc/free which is considerably more complex + * than mmap/munmap to call and can even require a valid + * thread context, it's difficult to implement __unmapself. + * + * Here we take an alternative approach which simply defers + * the deallocation. An obvious downside of this approach is + * that it keeps the stack longer. (possibly forever.) + * To avoid wasting too much memory, we only defer a single + * item at most. */ + process_map_base_deferred_free(); + map_base_deferred_free = self->map_base; // Can't use `exit()` here, because it is too high level return; } @@ -212,10 +222,15 @@ static void __pthread_exit(void *result) #ifdef __wasilibc_unmodified_upstream for (;;) __syscall(SYS_exit, 0); #else - // __syscall(SYS_exit) would unlock the thread, list - // do it manually here - __tl_unlock(); // Can't use `exit()` here, because it is too high level + + /* On Linux, the thread is created with CLONE_CHILD_CLEARTID, + * and the lock (__thread_list_lock) will be unlocked by kernel when + * this thread terminates. + * See also set_tid_address(2) + * + * In WebAssembly, we leave it to wasi_thread_start instead. + */ #endif } @@ -430,6 +445,14 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att if (map == MAP_FAILED) goto fail; } #else + /* Process the deferred free request if any before + * allocationg a new one. Hopefully it enables a reuse of the memory. + * + * Note: We can't perform a simple "handoff" becasue allocation + * sizes might be different. (eg. the stack size might differ) */ + __tl_lock(); + process_map_base_deferred_free(); + __tl_unlock(); map = malloc(size); if (!map) goto fail; #endif diff --git a/libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s b/libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s index 0fe9854..7a480b8 100644 --- a/libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s +++ b/libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s @@ -28,4 +28,21 @@ wasi_thread_start: local.get 1 # start_arg call __wasi_thread_start_C + # Unlock thread list. (as CLONE_CHILD_CLEARTID would do for Linux) + # + # Note: once we unlock the thread list, our "map_base" can be freed + # by a joining thread. It's safe as we are in ASM and no longer use + # our C stack or pthread_t. It's impossible to do this safely in C + # because there is no way to tell the C compiler not to use C stack. + i32.const __thread_list_lock + i32.const 0 + i32.atomic.store 0 + # As an optimization, we can check tl_lock_waiters here. + # But for now, simply wake up unconditionally as + # CLONE_CHILD_CLEARTID does. + i32.const __thread_list_lock + i32.const 1 + memory.atomic.notify 0 + drop + end_function |