diff options
29 files changed, 295 insertions, 116 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 +OBJDIR ?= $(CURDIR)/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, @@ -32,10 +32,14 @@ BULK_MEMORY_THRESHOLD ?= 32 # Variables from this point on are not meant to be overridable via the # make command-line. -# Set the target variables. Multiarch triples notably omit the vendor field, -# which happens to be what we do for the main target triple too. +# Set the default WASI target triple. TARGET_TRIPLE = wasm32-wasi -MULTIARCH_TRIPLE = wasm32-wasi + +# Threaded version necessitates a different traget, as objects from different +# targets can't be mixed together while linking. +ifeq ($(THREAD_MODEL), posix) +TARGET_TRIPLE = wasm32-wasi-threads +endif # These variables describe the locations of various files and directories in # the source tree. @@ -195,14 +199,22 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ $(addprefix $(LIBC_TOP_HALF_MUSL_SRC_DIR)/, \ env/__init_tls.c \ stdio/__lockfile.c \ + stdio/flockfile.c \ + stdio/ftrylockfile.c \ + stdio/funlockfile.c \ thread/__lock.c \ thread/__wait.c \ thread/__timedwait.c \ thread/default_attr.c \ thread/pthread_attr_destroy.c \ + thread/pthread_attr_get.c \ thread/pthread_attr_init.c \ thread/pthread_attr_setstack.c \ + thread/pthread_attr_setdetachstate.c \ thread/pthread_attr_setstacksize.c \ + thread/pthread_barrier_destroy.c \ + thread/pthread_barrier_init.c \ + thread/pthread_barrier_wait.c \ thread/pthread_cleanup_push.c \ thread/pthread_cond_broadcast.c \ thread/pthread_cond_destroy.c \ @@ -215,6 +227,8 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ thread/pthread_condattr_setclock.c \ thread/pthread_condattr_setpshared.c \ thread/pthread_create.c \ + thread/pthread_detach.c \ + thread/pthread_equal.c \ thread/pthread_getspecific.c \ thread/pthread_join.c \ thread/pthread_key_create.c \ @@ -256,6 +270,7 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ thread/sem_timedwait.c \ thread/sem_trywait.c \ thread/sem_wait.c \ + thread/wasm32/wasi_thread_start.s \ ) endif @@ -274,12 +289,13 @@ LIBC_TOP_HALF_HEADERS_PRIVATE = $(LIBC_TOP_HALF_DIR)/headers/private LIBC_TOP_HALF_SOURCES = $(LIBC_TOP_HALF_DIR)/sources LIBC_TOP_HALF_ALL_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SOURCES) \ - $(sort $(shell find $(LIBC_TOP_HALF_SOURCES) -name \*.c)) + $(sort $(shell find $(LIBC_TOP_HALF_SOURCES) -name \*.[cs])) # Add any extra flags CFLAGS = $(EXTRA_CFLAGS) # Set the target. CFLAGS += --target=$(TARGET_TRIPLE) +ASMFLAGS += --target=$(TARGET_TRIPLE) # WebAssembly floating-point match doesn't trap. # TODO: Add -fno-signaling-nans when the compiler supports it. CFLAGS += -fno-trapping-math @@ -326,10 +342,11 @@ 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)) DLMALLOC_OBJS = $(call objs,$(DLMALLOC_SOURCES)) EMMALLOC_OBJS = $(call objs,$(EMMALLOC_SOURCES)) LIBC_BOTTOM_HALF_ALL_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_ALL_SOURCES)) -LIBC_TOP_HALF_ALL_OBJS = $(call objs,$(LIBC_TOP_HALF_ALL_SOURCES)) +LIBC_TOP_HALF_ALL_OBJS = $(call asmobjs,$(call objs,$(LIBC_TOP_HALF_ALL_SOURCES))) ifeq ($(MALLOC_IMPL),dlmalloc) LIBC_OBJS += $(DLMALLOC_OBJS) else ifeq ($(MALLOC_IMPL),emmalloc) @@ -357,9 +374,9 @@ LIBWASI_EMULATED_SIGNAL_MUSL_OBJS = $(call objs,$(LIBWASI_EMULATED_SIGNAL_MUSL_S # These variables describe the locations of various files and # directories in the generated sysroot tree. -SYSROOT_LIB := $(SYSROOT)/lib/$(MULTIARCH_TRIPLE) +SYSROOT_LIB := $(SYSROOT)/lib/$(TARGET_TRIPLE) SYSROOT_INC = $(SYSROOT)/include -SYSROOT_SHARE = $(SYSROOT)/share/$(MULTIARCH_TRIPLE) +SYSROOT_SHARE = $(SYSROOT)/share/$(TARGET_TRIPLE) # Files from musl's include directory that we don't want to install in the # sysroot's include directory. @@ -504,6 +521,10 @@ $(OBJDIR)/%.o: $(CURDIR)/%.c include_dirs @mkdir -p "$(@D)" $(CC) $(CFLAGS) -MD -MP -o $@ -c $< +$(OBJDIR)/%.o: $(CURDIR)/%.s include_dirs + @mkdir -p "$(@D)" + $(CC) $(ASMFLAGS) -o $@ -c $< + -include $(shell find $(OBJDIR) -name \*.d) $(DLMALLOC_OBJS): CFLAGS += \ @@ -645,11 +666,13 @@ check-symbols: startup_files libc @# its builtin include path first, which produces compiler-specific @# output. @# - @# TODO: Undefine __FLOAT128__ for now since it's not in clang 8.0. - @# TODO: Filter out __FLT16_* for now, as not all versions of clang have these. @# 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: 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 + @# clang 16 for -mcpu=generic. + @# TODO: As of clang 16, __GNUC_VA_LIST is #defined without a value. $(CC) $(CFLAGS) "$(SYSROOT_SHARE)/include-all.c" \ -isystem $(SYSROOT_INC) \ -std=gnu17 \ @@ -663,22 +686,24 @@ check-symbols: startup_files libc -U__clang_version__ \ -U__clang_literal_encoding__ \ -U__clang_wide_literal_encoding__ \ + -U__wasm_mutable_globals__ \ + -U__wasm_sign_ext__ \ -U__GNUC__ \ -U__GNUC_MINOR__ \ -U__GNUC_PATCHLEVEL__ \ -U__VERSION__ \ - -U__FLOAT128__ \ -U__NO_MATH_ERRNO__ \ -U__BITINT_MAXWIDTH__ \ -U__FLT_EVAL_METHOD__ -Wno-builtin-macro-redefined \ | sed -e 's/__[[:upper:][:digit:]]*_ATOMIC_\([[:upper:][:digit:]_]*\)_LOCK_FREE/__compiler_ATOMIC_\1_LOCK_FREE/' \ - | grep -v '^#define __FLT16_' \ + | 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\)' \ > "$(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/$(MULTIARCH_TRIPLE)/$(THREAD_MODEL)" "$(SYSROOT_SHARE)" + diff -wur "$(CURDIR)/expected/$(TARGET_TRIPLE)" "$(SYSROOT_SHARE)" install: finish mkdir -p "$(INSTALL_DIR)" diff --git a/dlmalloc/src/malloc.c b/dlmalloc/src/malloc.c index 03da739..6e7a15c 100644 --- a/dlmalloc/src/malloc.c +++ b/dlmalloc/src/malloc.c @@ -5214,26 +5214,43 @@ static void internal_inspect_all(mstate m, /* ------------------ Exported try_init_allocator -------------------- */ /* Symbol marking the end of data, bss and explicit stack, provided by wasm-ld. */ -extern unsigned char __heap_base; +extern char __heap_base; +extern char __heap_end __attribute__((__weak__)); /* Initialize the initial state of dlmalloc to be able to use free memory between __heap_base and initial. */ static void try_init_allocator(void) { /* Check that it is a first-time initialization. */ assert(!is_initialized(gm)); - char *base = (char *)&__heap_base; - /* Calls sbrk(0) that returns the initial memory position. */ - char *init = (char *)CALL_MORECORE(0); - int initial_heap_size = init - base; + /* Initialize mstate. */ + ensure_initialization(); + + char *base = &__heap_base; + // Try to use the linker pseudo-symbol `__heap_end` for the initial size of + // the heap, but if that's not defined due to LLVM being too old perhaps then + // round up `base` to the nearest `PAGESIZE`. The initial size of linear + // memory will be at least the heap base to this page boundary, and it's then + // assumed that the initial linear memory image was truncated at that point. + // While this reflects the default behavior of `wasm-ld` it is also possible + // for users to craft larger linear memories by passing options to extend + // beyond this threshold. In this situation the memory will not be used for + // dlmalloc. + // + // Note that `sbrk(0)`, or in dlmalloc-ese `CALL_MORECORE(0)`, is specifically + // not used here. That captures the current size of the heap but is only + // correct if the we're the first to try to grow the heap. If the heap has + // grown elsewhere, such as a different allocator in place, then this would + // incorrectly claim such memroy as our own. + char *end = &__heap_end; + if (end == NULL) + end = (char*) page_align((size_t) base); + size_t initial_heap_size = end - base; /* Check that initial heap is long enough to serve a minimal allocation request. */ if (initial_heap_size <= MIN_CHUNK_SIZE + TOP_FOOT_SIZE + MALLOC_ALIGNMENT) { return; } - /* Initialize mstate. */ - ensure_initialization(); - /* Initialize the dlmalloc internal state. */ gm->least_addr = base; gm->seg.base = base; diff --git a/expected/wasm32-wasi/posix/defined-symbols.txt b/expected/wasm32-wasi-threads/defined-symbols.txt index 6e565dc..8dba0f1 100644 --- a/expected/wasm32-wasi/posix/defined-symbols.txt +++ b/expected/wasm32-wasi-threads/defined-symbols.txt @@ -46,6 +46,7 @@ __do_cleanup_pop __do_cleanup_push __do_des __do_orphaned_stdio_locks +__dummy_reference __duplocale __env_rm_add __errno_location @@ -193,7 +194,6 @@ __progname __progname_full __pthread_cond_timedwait __pthread_create -__pthread_exit __pthread_join __pthread_key_create __pthread_key_delete @@ -221,6 +221,7 @@ __qsort_r __rand48_step __random_lockptr __reallocarray +__register_locked_file __release_ptc __rem_pio2 __rem_pio2_large @@ -354,6 +355,7 @@ __wasi_sock_recv __wasi_sock_send __wasi_sock_shutdown __wasi_thread_spawn +__wasi_thread_start_C __wasilibc_access __wasilibc_cwd __wasilibc_cwd_lock @@ -649,6 +651,7 @@ fileno fileno_unlocked finite finitef +flockfile floor floorf floorl @@ -704,6 +707,8 @@ ftello ftello64 ftime ftruncate +ftrylockfile +funlockfile futimens futimesat fwide @@ -972,9 +977,20 @@ program_invocation_short_name pselect psignal pthread_attr_destroy +pthread_attr_getdetachstate +pthread_attr_getguardsize +pthread_attr_getinheritsched +pthread_attr_getscope +pthread_attr_getstack +pthread_attr_getstacksize pthread_attr_init +pthread_attr_setdetachstate pthread_attr_setstack pthread_attr_setstacksize +pthread_barrier_destroy +pthread_barrier_init +pthread_barrier_wait +pthread_barrierattr_getpshared pthread_cond_broadcast pthread_cond_destroy pthread_cond_init @@ -982,11 +998,13 @@ pthread_cond_signal pthread_cond_timedwait pthread_cond_wait pthread_condattr_destroy +pthread_condattr_getpshared pthread_condattr_init pthread_condattr_setclock pthread_condattr_setpshared pthread_create -pthread_exit +pthread_detach +pthread_equal pthread_getspecific pthread_join pthread_key_create @@ -1000,6 +1018,10 @@ pthread_mutex_timedlock pthread_mutex_trylock pthread_mutex_unlock pthread_mutexattr_destroy +pthread_mutexattr_getprotocol +pthread_mutexattr_getpshared +pthread_mutexattr_getrobust +pthread_mutexattr_gettype pthread_mutexattr_init pthread_mutexattr_setprotocol pthread_mutexattr_setpshared @@ -1016,6 +1038,7 @@ pthread_rwlock_trywrlock pthread_rwlock_unlock pthread_rwlock_wrlock pthread_rwlockattr_destroy +pthread_rwlockattr_getpshared pthread_rwlockattr_init pthread_rwlockattr_setpshared pthread_self @@ -1211,6 +1234,8 @@ tgamma tgammaf tgammal thrd_current +thrd_detach +thrd_equal thrd_sleep time timegm diff --git a/expected/wasm32-wasi/posix/include-all.c b/expected/wasm32-wasi-threads/include-all.c index 0b43b07..0b43b07 100644 --- a/expected/wasm32-wasi/posix/include-all.c +++ b/expected/wasm32-wasi-threads/include-all.c diff --git a/expected/wasm32-wasi/posix/predefined-macros.txt b/expected/wasm32-wasi-threads/predefined-macros.txt index ac90920..a9f2982 100644 --- a/expected/wasm32-wasi/posix/predefined-macros.txt +++ b/expected/wasm32-wasi-threads/predefined-macros.txt @@ -2489,6 +2489,7 @@ #define __DEFINED_wctype_t #define __DEFINED_wint_t #define __FINITE_MATH_ONLY__ 0 +#define __FLOAT128__ 1 #define __FLT(x) (__IS_REAL(x) && sizeof(x) == sizeof(float)) #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) #define __FLT_DECIMAL_DIG__ 9 @@ -3099,8 +3100,6 @@ #define __wasm__ 1 #define __wasm_atomics__ 1 #define __wasm_bulk_memory__ 1 -#define __wasm_mutable_globals__ 1 -#define __wasm_sign_ext__ 1 #define _tolower(a) ((a)|0x20) #define _toupper(a) ((a)&0x5f) #define acos(x) __tg_real_complex(acos, (x)) diff --git a/expected/wasm32-wasi/posix/undefined-symbols.txt b/expected/wasm32-wasi-threads/undefined-symbols.txt index 7def0a9..a685151 100644 --- a/expected/wasm32-wasi/posix/undefined-symbols.txt +++ b/expected/wasm32-wasi-threads/undefined-symbols.txt @@ -1,4 +1,5 @@ __addtf3 +__data_end __divtf3 __eqtf2 __extenddftf2 @@ -9,6 +10,7 @@ __fixunstfsi __floatsitf __floatunsitf __getf2 +__global_base __gttf2 __heap_base __imported_wasi_snapshot_preview1_args_get diff --git a/expected/wasm32-wasi/single/defined-symbols.txt b/expected/wasm32-wasi/defined-symbols.txt index a692632..a692632 100644 --- a/expected/wasm32-wasi/single/defined-symbols.txt +++ b/expected/wasm32-wasi/defined-symbols.txt diff --git a/expected/wasm32-wasi/single/include-all.c b/expected/wasm32-wasi/include-all.c index 86297f3..86297f3 100644 --- a/expected/wasm32-wasi/single/include-all.c +++ b/expected/wasm32-wasi/include-all.c diff --git a/expected/wasm32-wasi/single/predefined-macros.txt b/expected/wasm32-wasi/predefined-macros.txt index 1f7b341..234ad3c 100644 --- a/expected/wasm32-wasi/single/predefined-macros.txt +++ b/expected/wasm32-wasi/predefined-macros.txt @@ -2123,7 +2123,6 @@ #define _POSIX_STREAM_MAX 8 #define _POSIX_SYMLINK_MAX 255 #define _POSIX_SYMLOOP_MAX 8 -#define _POSIX_THREADS _POSIX_VERSION #define _POSIX_THREAD_ATTR_STACKADDR _POSIX_VERSION #define _POSIX_THREAD_ATTR_STACKSIZE _POSIX_VERSION #define _POSIX_THREAD_CPUTIME _POSIX_VERSION @@ -2453,6 +2452,7 @@ #define __DEFINED_wctype_t #define __DEFINED_wint_t #define __FINITE_MATH_ONLY__ 0 +#define __FLOAT128__ 1 #define __FLT(x) (__IS_REAL(x) && sizeof(x) == sizeof(float)) #define __FLTCX(x) (__IS_CX(x) && sizeof(x) == sizeof(float complex)) #define __FLT_DECIMAL_DIG__ 9 diff --git a/expected/wasm32-wasi/single/undefined-symbols.txt b/expected/wasm32-wasi/undefined-symbols.txt index b9cdb34..b9cdb34 100644 --- a/expected/wasm32-wasi/single/undefined-symbols.txt +++ b/expected/wasm32-wasi/undefined-symbols.txt diff --git a/libc-bottom-half/headers/public/__struct_sockaddr.h b/libc-bottom-half/headers/public/__struct_sockaddr.h index 9891b90..2e14d95 100644 --- a/libc-bottom-half/headers/public/__struct_sockaddr.h +++ b/libc-bottom-half/headers/public/__struct_sockaddr.h @@ -1,9 +1,6 @@ #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 { diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_in.h b/libc-bottom-half/headers/public/__struct_sockaddr_in.h index 73dc5c6..5d4ce51 100644 --- a/libc-bottom-half/headers/public/__struct_sockaddr_in.h +++ b/libc-bottom-half/headers/public/__struct_sockaddr_in.h @@ -1,15 +1,12 @@ #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; + __attribute__((aligned(__BIGGEST_ALIGNMENT__))) sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; }; diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_in6.h b/libc-bottom-half/headers/public/__struct_sockaddr_in6.h index a220f91..98703dc 100644 --- a/libc-bottom-half/headers/public/__struct_sockaddr_in6.h +++ b/libc-bottom-half/headers/public/__struct_sockaddr_in6.h @@ -1,15 +1,12 @@ #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; + __attribute__((aligned(__BIGGEST_ALIGNMENT__))) sa_family_t sin6_family; in_port_t sin6_port; unsigned sin6_flowinfo; struct in6_addr sin6_addr; diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_storage.h b/libc-bottom-half/headers/public/__struct_sockaddr_storage.h index 1ae26a7..b4ebad2 100644 --- a/libc-bottom-half/headers/public/__struct_sockaddr_storage.h +++ b/libc-bottom-half/headers/public/__struct_sockaddr_storage.h @@ -1,13 +1,10 @@ #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; + __attribute__((aligned(__BIGGEST_ALIGNMENT__))) sa_family_t ss_family; char __ss_data[32]; }; diff --git a/libc-bottom-half/headers/public/__struct_sockaddr_un.h b/libc-bottom-half/headers/public/__struct_sockaddr_un.h index a5be6e5..6371194 100644 --- a/libc-bottom-half/headers/public/__struct_sockaddr_un.h +++ b/libc-bottom-half/headers/public/__struct_sockaddr_un.h @@ -1,13 +1,10 @@ #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; + __attribute__((aligned(__BIGGEST_ALIGNMENT__))) sa_family_t sun_family; }; #endif diff --git a/libc-bottom-half/headers/public/wasi/api.h b/libc-bottom-half/headers/public/wasi/api.h index 1ab7699..45a6506 100644 --- a/libc-bottom-half/headers/public/wasi/api.h +++ b/libc-bottom-half/headers/public/wasi/api.h @@ -2099,7 +2099,7 @@ __wasi_errno_t __wasi_sock_shutdown( * * @see https://github.com/WebAssembly/wasi-threads/#readme */ -__wasi_errno_t __wasi_thread_spawn( +int32_t __wasi_thread_spawn( /** * A pointer to an opaque struct to be passed to the module's entry * function. diff --git a/libc-bottom-half/sources/__wasilibc_real.c b/libc-bottom-half/sources/__wasilibc_real.c index 855a2c6..d2e6b71 100644 --- a/libc-bottom-half/sources/__wasilibc_real.c +++ b/libc-bottom-half/sources/__wasilibc_real.c @@ -665,8 +665,7 @@ int32_t __imported_wasi_thread_spawn(int32_t arg0) __attribute__(( __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; +int32_t __wasi_thread_spawn(void* start_arg) { + return __imported_wasi_thread_spawn((int32_t) start_arg); } #endif diff --git a/libc-bottom-half/sources/posix.c b/libc-bottom-half/sources/posix.c index 35dd99b..b3e59ec 100644 --- a/libc-bottom-half/sources/posix.c +++ b/libc-bottom-half/sources/posix.c @@ -58,9 +58,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -71,9 +71,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -88,9 +88,9 @@ ssize_t readlink( 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -101,9 +101,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -114,9 +114,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -127,9 +127,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -147,9 +147,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -169,9 +169,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -185,9 +185,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -198,21 +198,21 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; 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)) { + if (r != 0 && (errno == EISDIR || errno == ENOENT)) { // 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; + errno = ENOENT; } return r; } @@ -221,9 +221,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -234,9 +234,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return NULL; } @@ -252,9 +252,9 @@ int scandir( 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -265,9 +265,9 @@ 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -287,8 +287,8 @@ int link(const char *old, const char *new) { new_dirfd, new_relative_path, 0); } - // We couldn't find a preopen for it; indicate that we lack capabilities. - errno = ENOTCAPABLE; + // We couldn't find a preopen for it; fail as if we can't find the path. + errno = ENOENT; return -1; } @@ -305,8 +305,8 @@ int rename(const char *old, const char *new) { new_dirfd, new_relative_path); } - // We couldn't find a preopen for it; indicate that we lack capabilities. - errno = ENOTCAPABLE; + // We couldn't find a preopen for it; fail as if we can't find the path. + errno = ENOENT; return -1; } @@ -317,9 +317,9 @@ __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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -334,9 +334,9 @@ __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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -351,9 +351,9 @@ __wasilibc_stat(const char *__restrict path, struct stat *__restrict st, int fla 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 we can't find a preopen for it, fail as if we can't find the path. if (dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -369,9 +369,9 @@ __wasilibc_link(const char *oldpath, const char *newpath, int flags) 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 we can't find a preopen for it, fail as if we can't find the path. if (old_dirfd == -1 || new_dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -387,9 +387,9 @@ __wasilibc_link_oldat(int olddirfd, const char *oldpath, const char *newpath, in 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 we can't find a preopen for it, fail as if we can't find the path. if (new_dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -405,9 +405,9 @@ __wasilibc_link_newat(const char *oldpath, int newdirfd, const char *newpath, in 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 we can't find a preopen for it, fail as if we can't find the path. if (old_dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -423,9 +423,9 @@ __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 we can't find a preopen for it, fail as if we can't find the path. if (to_dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } @@ -439,9 +439,9 @@ __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 we can't find a preopen for it, fail as if we can't find the path. if (from_dirfd == -1) { - errno = ENOTCAPABLE; + errno = ENOENT; return -1; } diff --git a/libc-top-half/musl/include/pthread.h b/libc-top-half/musl/include/pthread.h index 01fe5f2..b14fe82 100644 --- a/libc-top-half/musl/include/pthread.h +++ b/libc-top-half/musl/include/pthread.h @@ -85,7 +85,9 @@ extern "C" { int pthread_create(pthread_t *__restrict, const pthread_attr_t *__restrict, void *(*)(void *), void *__restrict); int pthread_detach(pthread_t); +#ifdef __wasilibc_unmodified_upstream _Noreturn void pthread_exit(void *); +#endif int pthread_join(pthread_t, void **); #ifdef __GNUC__ diff --git a/libc-top-half/musl/include/unistd.h b/libc-top-half/musl/include/unistd.h index b5cb5c6..0be83e3 100644 --- a/libc-top-half/musl/include/unistd.h +++ b/libc-top-half/musl/include/unistd.h @@ -336,7 +336,9 @@ pid_t gettid(void); #endif #define _POSIX_VDISABLE 0 +#if defined(__wasilibc_unmodified_upstream) || defined(_REENTRANT) #define _POSIX_THREADS _POSIX_VERSION +#endif #define _POSIX_THREAD_PROCESS_SHARED _POSIX_VERSION #define _POSIX_THREAD_SAFE_FUNCTIONS _POSIX_VERSION #define _POSIX_THREAD_ATTR_STACKADDR _POSIX_VERSION diff --git a/libc-top-half/musl/src/env/__init_tls.c b/libc-top-half/musl/src/env/__init_tls.c index ee785bc..ece8d24 100644 --- a/libc-top-half/musl/src/env/__init_tls.c +++ b/libc-top-half/musl/src/env/__init_tls.c @@ -16,6 +16,43 @@ volatile int __thread_list_lock; #ifndef __wasilibc_unmodified_upstream + +/* These symbols are generated by wasm-ld. __stack_high/__stack_low + * symbols are only available in LLVM v16 and higher, therefore they're + * defined as weak symbols and if not available, __heap_base/__data_end + * is used instead. + * + * TODO: remove usage of __heap_base/__data_end for stack size calculation + * once we drop support for LLVM v15 and older. + */ +extern unsigned char __heap_base; +extern unsigned char __data_end; +extern unsigned char __global_base; +extern weak unsigned char __stack_high; +extern weak unsigned char __stack_low; + +static inline void setup_default_stack_size() +{ + ptrdiff_t stack_size; + + if (&__stack_high) + stack_size = &__stack_high - &__stack_low; + else { + unsigned char *sp; + __asm__( + ".globaltype __stack_pointer, i32\n" + "global.get __stack_pointer\n" + "local.set %0\n" + : "=r"(sp)); + 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; +} + void __wasi_init_tp() { __init_tp((void *)__get_tp()); } @@ -31,6 +68,20 @@ int __init_tp(void *p) if (!r) libc.can_do_threads = 1; td->detach_state = DT_JOINABLE; td->tid = __syscall(SYS_set_tid_address, &__thread_list_lock); +#else + setup_default_stack_size(); + td->detach_state = DT_JOINABLE; + /* + * Initialize the TID to a value which doesn't conflict with + * host-allocated TIDs, so that TID-based locks can work. + * + * Note: + * - Host-allocated TIDs range from 1 to 0x1fffffff. (inclusive) + * - __tl_lock and __lockfile uses TID 0 as "unlocked". + * - __lockfile relies on the fact the most significant two bits + * of TIDs are 0. + */ + td->tid = 0x3fffffff; #endif td->locale = &libc.global_locale; td->robust_list.head = &td->robust_list.head; diff --git a/libc-top-half/musl/src/include/pthread.h b/libc-top-half/musl/src/include/pthread.h index 7167d3e..0aba813 100644 --- a/libc-top-half/musl/src/include/pthread.h +++ b/libc-top-half/musl/src/include/pthread.h @@ -7,7 +7,9 @@ hidden int __pthread_once(pthread_once_t *, void (*)(void)); hidden void __pthread_testcancel(void); hidden int __pthread_setcancelstate(int, int *); hidden int __pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict); +#ifdef __wasilibc_unmodified_upstream hidden _Noreturn void __pthread_exit(void *); +#endif hidden int __pthread_join(pthread_t, void **); hidden int __pthread_mutex_lock(pthread_mutex_t *); hidden int __pthread_mutex_trylock(pthread_mutex_t *); diff --git a/libc-top-half/musl/src/internal/pthread_impl.h b/libc-top-half/musl/src/internal/pthread_impl.h index a6d188b..1e7b974 100644 --- a/libc-top-half/musl/src/internal/pthread_impl.h +++ b/libc-top-half/musl/src/internal/pthread_impl.h @@ -216,7 +216,12 @@ extern hidden unsigned __default_stacksize; extern hidden unsigned __default_guardsize; #define DEFAULT_STACK_SIZE 131072 +#ifdef __wasilibc_unmodified_upstream #define DEFAULT_GUARD_SIZE 8192 +#else +/* guard doesn't make much sense without mprotect. */ +#define DEFAULT_GUARD_SIZE 0 +#endif #define DEFAULT_STACK_MAX (8<<20) #define DEFAULT_GUARD_MAX (1<<20) diff --git a/libc-top-half/musl/src/thread/__wait.c b/libc-top-half/musl/src/thread/__wait.c index c0e4aac..7ffa987 100644 --- a/libc-top-half/musl/src/thread/__wait.c +++ b/libc-top-half/musl/src/thread/__wait.c @@ -48,7 +48,7 @@ void __wait(volatile int *addr, volatile int *waiters, int val, int priv) __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS || __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0); #else - __wasilibc_futex_wait(addr, FUTEX_WAIT, val, 0); + __wasilibc_futex_wait(addr, FUTEX_WAIT, val, -1); #endif } if (waiters) a_dec(waiters); diff --git a/libc-top-half/musl/src/thread/pthread_attr_get.c b/libc-top-half/musl/src/thread/pthread_attr_get.c index f12ff44..0ac251c 100644 --- a/libc-top-half/musl/src/thread/pthread_attr_get.c +++ b/libc-top-half/musl/src/thread/pthread_attr_get.c @@ -17,6 +17,7 @@ int pthread_attr_getinheritsched(const pthread_attr_t *restrict a, int *restrict return 0; } +#ifdef __wasilibc_unmodified_upstream /* WASI has no CPU scheduling support. */ int pthread_attr_getschedparam(const pthread_attr_t *restrict a, struct sched_param *restrict param) { param->sched_priority = a->_a_prio; @@ -28,6 +29,7 @@ int pthread_attr_getschedpolicy(const pthread_attr_t *restrict a, int *restrict *policy = a->_a_policy; return 0; } +#endif int pthread_attr_getscope(const pthread_attr_t *restrict a, int *restrict scope) { @@ -56,11 +58,13 @@ int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict a, int return 0; } +#ifdef __wasilibc_unmodified_upstream /* Forward declaration of WASI's `__clockid` type. */ int pthread_condattr_getclock(const pthread_condattr_t *restrict a, clockid_t *restrict clk) { *clk = a->__attr & 0x7fffffff; return 0; } +#endif int pthread_condattr_getpshared(const pthread_condattr_t *restrict a, int *restrict pshared) { diff --git a/libc-top-half/musl/src/thread/pthread_barrier_destroy.c b/libc-top-half/musl/src/thread/pthread_barrier_destroy.c index 4ce0b2e..a347a2c 100644 --- a/libc-top-half/musl/src/thread/pthread_barrier_destroy.c +++ b/libc-top-half/musl/src/thread/pthread_barrier_destroy.c @@ -9,7 +9,9 @@ int pthread_barrier_destroy(pthread_barrier_t *b) while ((v = b->_b_lock) & INT_MAX) __wait(&b->_b_lock, 0, v, 0); } +#ifdef __wasilibc_unmodified_upstream /* WASI does not understand processes or locking between them. */ __vm_wait(); +#endif } return 0; } diff --git a/libc-top-half/musl/src/thread/pthread_barrier_wait.c b/libc-top-half/musl/src/thread/pthread_barrier_wait.c index cc2a8bb..0891b71 100644 --- a/libc-top-half/musl/src/thread/pthread_barrier_wait.c +++ b/libc-top-half/musl/src/thread/pthread_barrier_wait.c @@ -23,7 +23,9 @@ static int pshared_barrier_wait(pthread_barrier_t *b) __wait(&b->_b_count, &b->_b_waiters2, v, 0); } +#ifdef __wasilibc_unmodified_upstream /* WASI does not understand processes or locking between them. */ __vm_lock(); +#endif /* Ensure all threads have a vm lock before proceeding */ if (a_fetch_add(&b->_b_count, -1)==1-limit) { @@ -44,7 +46,9 @@ static int pshared_barrier_wait(pthread_barrier_t *b) if (v==INT_MIN+1 || (v==1 && w)) __wake(&b->_b_lock, 1, 0); +#ifdef __wasilibc_unmodified_upstream /* WASI does not understand processes or locking between them. */ __vm_unlock(); +#endif return ret; } @@ -84,8 +88,12 @@ int pthread_barrier_wait(pthread_barrier_t *b) a_spin(); a_inc(&inst->finished); while (inst->finished == 1) +#ifdef __wasilibc_unmodified_upstream __syscall(SYS_futex,&inst->finished,FUTEX_WAIT|FUTEX_PRIVATE,1,0) != -ENOSYS || __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0); +#else + __futexwait(&inst->finished, 1, 0); +#endif return PTHREAD_BARRIER_SERIAL_THREAD; } diff --git a/libc-top-half/musl/src/thread/pthread_create.c b/libc-top-half/musl/src/thread/pthread_create.c index 1aa7be7..676e2cc 100644 --- a/libc-top-half/musl/src/thread/pthread_create.c +++ b/libc-top-half/musl/src/thread/pthread_create.c @@ -60,7 +60,11 @@ void __tl_sync(pthread_t td) if (tl_lock_waiters) __wake(&__thread_list_lock, 1, 0); } +#ifdef __wasilibc_unmodified_upstream _Noreturn void __pthread_exit(void *result) +#else +static void __pthread_exit(void *result) +#endif { pthread_t self = __pthread_self(); sigset_t set; @@ -191,7 +195,7 @@ _Noreturn void __pthread_exit(void *result) __tl_unlock(); free(self->map_base); // Can't use `exit()` here, because it is too high level - for (;;) __wasi_proc_exit(0); + return; } #endif @@ -212,7 +216,6 @@ _Noreturn void __pthread_exit(void *result) // do it manually here __tl_unlock(); // Can't use `exit()` here, because it is too high level - for (;;) __wasi_proc_exit(0); #endif } @@ -235,9 +238,14 @@ struct start_args { volatile int control; unsigned long sig_mask[_NSIG/8/sizeof(long)]; #else + /* + * Note: the offset of the "stack" and "tls_base" members + * in this structure is hardcoded in wasi_thread_start. + */ + void *stack; + void *tls_base; void *(*start_func)(void *); void *start_arg; - void *tls_base; #endif }; @@ -271,32 +279,41 @@ static int start_c11(void *p) return 0; } #else -__attribute__((export_name("wasi_thread_start"))) -_Noreturn void wasi_thread_start(int tid, void *p) + +/* + * We want to ensure wasi_thread_start is linked whenever + * pthread_create is used. The following reference is to ensure that. + * Otherwise, the linker doesn't notice the dependency because + * wasi_thread_start is used indirectly via a wasm export. + */ +void wasi_thread_start(int tid, void *p); +hidden void *__dummy_reference = wasi_thread_start; + +hidden void __wasi_thread_start_C(int tid, void *p) { struct start_args *args = p; - __asm__(".globaltype __tls_base, i32\n" - "local.get %0\n" - "global.set __tls_base\n" - :: "r"(args->tls_base)); pthread_t self = __pthread_self(); // Set the thread ID (TID) on the pthread structure. The TID is stored // atomically since it is also stored by the parent thread; this way, // whichever thread (parent or child) reaches this point first can proceed // without waiting. atomic_store((atomic_int *) &(self->tid), tid); - // Set the stack pointer. - __asm__(".globaltype __stack_pointer, i32\n" - "local.get %0\n" - "global.set __stack_pointer\n" - :: "r"(self->stack)); // Execute the user's start function. - int (*start)(void*) = (int(*)(void*)) args->start_func; - __pthread_exit((void *)(uintptr_t)start(args->start_arg)); + __pthread_exit(args->start_func(args->start_arg)); } #endif +#ifdef __wasilibc_unmodified_upstream #define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE) +#else +/* + * As we allocate stack with malloc() instead of mmap/mprotect, + * there is no point to round it up to PAGE_SIZE. + * Instead, round up to a sane alignment. + * Note: PAGE_SIZE is rather big on WASM. (65536) + */ +#define ROUND(x) (((x)+16-1)&-16) +#endif /* pthread_key_create.c overrides this */ static volatile size_t dummy = 0; @@ -484,6 +501,7 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att /* Correct the stack size */ new->stack_size = stack - stack_limit; + args->stack = new->stack; /* just for convenience of asm trampoline */ args->start_func = entry; args->start_arg = arg; args->tls_base = (void*)new_tls_base; @@ -561,5 +579,7 @@ fail: return EAGAIN; } +#ifdef __wasilibc_unmodified_upstream weak_alias(__pthread_exit, pthread_exit); +#endif weak_alias(__pthread_create, pthread_create); 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 new file mode 100644 index 0000000..0fe9854 --- /dev/null +++ b/libc-top-half/musl/src/thread/wasm32/wasi_thread_start.s @@ -0,0 +1,31 @@ + .text + + .export_name wasi_thread_start, wasi_thread_start + + .globaltype __stack_pointer, i32 + .globaltype __tls_base, i32 + .functype __wasi_thread_start_C (i32, i32) -> () + + .hidden wasi_thread_start + .globl wasi_thread_start + .type wasi_thread_start,@function + +wasi_thread_start: + .functype wasi_thread_start (i32, i32) -> () + + # Set up the minimum C environment. + # Note: offsetof(start_arg, stack) == 0 + local.get 1 # start_arg + i32.load 0 # stack + global.set __stack_pointer + + local.get 1 # start_arg + i32.load 4 # tls_base + global.set __tls_base + + # Make the C function do the rest of work. + local.get 0 # tid + local.get 1 # start_arg + call __wasi_thread_start_C + + end_function |