diff --git a/Makefile b/Makefile index f7dde2740..ab49cb771 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,9 @@ LIBC_BOTTOM_HALF_OMIT_SOURCES := \ $(LIBC_BOTTOM_HALF_SOURCES)/accept-wasip2.c LIBC_BOTTOM_HALF_ALL_SOURCES := $(filter-out $(LIBC_BOTTOM_HALF_OMIT_SOURCES),$(LIBC_BOTTOM_HALF_ALL_SOURCES)) # Omit p2-specific headers from include-all.c test. -INCLUDE_ALL_CLAUSES := -not -name wasip2.h -not -name descriptor_table.h +# setjmp.h is excluded because it requires a different compiler option +# for exception-handling. +INCLUDE_ALL_CLAUSES := -not -name wasip2.h -not -name descriptor_table.h -not -name setjmp.h endif ifeq ($(WASI_SNAPSHOT), p2) @@ -123,6 +125,7 @@ LIBWASI_EMULATED_SIGNAL_MUSL_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/signal/psignal.c \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/string/strsignal.c LIBDL_SOURCES = $(LIBC_TOP_HALF_MUSL_SRC_DIR)/misc/dl.c +LIBSETJMP_SOURCES = $(LIBC_TOP_HALF_MUSL_SRC_DIR)/setjmp/wasm32/rt.c LIBC_BOTTOM_HALF_CRT_SOURCES = $(wildcard $(LIBC_BOTTOM_HALF_DIR)/crt/*.c) LIBC_TOP_HALF_DIR = libc-top-half LIBC_TOP_HALF_MUSL_DIR = $(LIBC_TOP_HALF_DIR)/musl @@ -428,6 +431,7 @@ 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)) LIBDL_OBJS = $(call objs,$(LIBDL_SOURCES)) +LIBSETJMP_OBJS = $(call objs,$(LIBSETJMP_SOURCES)) LIBC_BOTTOM_HALF_CRT_OBJS = $(call objs,$(LIBC_BOTTOM_HALF_CRT_SOURCES)) # These variables describe the locations of various files and @@ -493,7 +497,6 @@ MUSL_OMIT_HEADERS += \ "netdb.h" \ "resolv.h" \ "pty.h" \ - "setjmp.h" \ "ulimit.h" \ "sys/xattr.h" \ "wordexp.h" \ @@ -529,6 +532,7 @@ LIBWASI_EMULATED_GETPID_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_GETP LIBWASI_EMULATED_SIGNAL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_SIGNAL_OBJS)) LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS)) LIBDL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBDL_OBJS)) +LIBSETJMP_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBSETJMP_OBJS)) BULK_MEMORY_SO_OBJS = $(patsubst %.o,%.pic.o,$(BULK_MEMORY_OBJS)) DLMALLOC_SO_OBJS = $(patsubst %.o,%.pic.o,$(DLMALLOC_OBJS)) LIBC_BOTTOM_HALF_ALL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBC_BOTTOM_HALF_ALL_OBJS)) @@ -543,6 +547,7 @@ PIC_OBJS = \ $(LIBWASI_EMULATED_SIGNAL_SO_OBJS) \ $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS) \ $(LIBDL_SO_OBJS) \ + $(LIBSETJMP_SO_OBJS) \ $(BULK_MEMORY_SO_OBJS) \ $(DLMALLOC_SO_OBJS) \ $(LIBC_BOTTOM_HALF_ALL_SO_OBJS) \ @@ -572,6 +577,8 @@ $(OBJDIR)/libwasi-emulated-signal.so.a: $(LIBWASI_EMULATED_SIGNAL_SO_OBJS) $(LIB $(OBJDIR)/libdl.so.a: $(LIBDL_SO_OBJS) +$(OBJDIR)/libsetjmp.so.a: $(LIBSETJMP_SO_OBJS) + $(SYSROOT_LIB)/libc.a: $(LIBC_OBJS) $(SYSROOT_LIB)/libc-printscan-long-double.a: $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) @@ -588,6 +595,8 @@ $(SYSROOT_LIB)/libwasi-emulated-signal.a: $(LIBWASI_EMULATED_SIGNAL_OBJS) $(LIBW $(SYSROOT_LIB)/libdl.a: $(LIBDL_OBJS) +$(SYSROOT_LIB)/libsetjmp.a: $(LIBSETJMP_OBJS) + %.a: @mkdir -p "$(@D)" # On Windows, the commandline for the ar invocation got too long, so it needs to be split up. @@ -617,6 +626,9 @@ $(BULK_MEMORY_OBJS) $(BULK_MEMORY_SO_OBJS): CFLAGS += \ $(BULK_MEMORY_OBJS) $(BULK_MEMORY_SO_OBJS): CFLAGS += \ -DBULK_MEMORY_THRESHOLD=$(BULK_MEMORY_THRESHOLD) +$(LIBSETJMP_OBJS) $(LIBSETJMP_SO_OBJS): CFLAGS += \ + -mllvm -wasm-enable-sjlj + $(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS): CFLAGS += \ -D_WASI_EMULATED_SIGNAL @@ -660,7 +672,7 @@ startup_files $(LIBC_BOTTOM_HALF_ALL_OBJS) $(LIBC_BOTTOM_HALF_ALL_SO_OBJS): CFLA -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/include \ -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal -$(LIBC_TOP_HALF_ALL_OBJS) $(LIBC_TOP_HALF_ALL_SO_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_SO_OBJS) $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS) $(LIBDL_OBJS) $(LIBDL_SO_OBJS): CFLAGS += \ +$(LIBC_TOP_HALF_ALL_OBJS) $(LIBC_TOP_HALF_ALL_SO_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_OBJS) $(MUSL_PRINTSCAN_LONG_DOUBLE_SO_OBJS) $(MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS) $(LIBDL_OBJS) $(LIBDL_SO_OBJS) $(LIBSETJMP_OBJS) $(LIBSETJMP_SO_OBJS): CFLAGS += \ -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/include \ -I$(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal \ -I$(LIBC_TOP_HALF_MUSL_DIR)/arch/wasm32 \ @@ -726,7 +738,8 @@ LIBC_SO = \ $(SYSROOT_LIB)/libwasi-emulated-process-clocks.so \ $(SYSROOT_LIB)/libwasi-emulated-getpid.so \ $(SYSROOT_LIB)/libwasi-emulated-signal.so \ - $(SYSROOT_LIB)/libdl.so + $(SYSROOT_LIB)/libdl.so \ + $(SYSROOT_LIB)/libsetjmp.so endif libc_so: include_dirs $(LIBC_SO) @@ -739,7 +752,8 @@ libc: include_dirs \ $(SYSROOT_LIB)/libwasi-emulated-process-clocks.a \ $(SYSROOT_LIB)/libwasi-emulated-getpid.a \ $(SYSROOT_LIB)/libwasi-emulated-signal.a \ - $(SYSROOT_LIB)/libdl.a + $(SYSROOT_LIB)/libdl.a \ + $(SYSROOT_LIB)/libsetjmp.a finish: startup_files libc # diff --git a/libc-top-half/musl/arch/wasm32/bits/setjmp.h b/libc-top-half/musl/arch/wasm32/bits/setjmp.h new file mode 100644 index 000000000..63973a800 --- /dev/null +++ b/libc-top-half/musl/arch/wasm32/bits/setjmp.h @@ -0,0 +1 @@ +typedef unsigned long __jmp_buf[8]; diff --git a/libc-top-half/musl/include/setjmp.h b/libc-top-half/musl/include/setjmp.h index f505f8e4f..5f90c97b8 100644 --- a/libc-top-half/musl/include/setjmp.h +++ b/libc-top-half/musl/include/setjmp.h @@ -7,8 +7,12 @@ extern "C" { #include -#ifdef __wasilibc_unmodified_upstream /* WASI has no setjmp */ -#include +/* WASI has no setjmp */ +#ifndef __wasilibc_unmodified_upstream +#if !defined(__wasm_exception_handling) +#error Setjmp/longjmp support requires Exception handling support, which is [not yet standardized](https://github.com/WebAssembly/proposals?tab=readme-ov-file#phase-3---implementation-phase-cg--wg). To enable it, compile with `-mllvm -wasm-enable-sjlj` and use an engine that implements the Exception handling proposal. +#endif +#endif typedef struct __jmp_buf_tag { __jmp_buf __jb; diff --git a/libc-top-half/musl/src/setjmp/wasm32/rt.c b/libc-top-half/musl/src/setjmp/wasm32/rt.c new file mode 100644 index 000000000..24e4e3361 --- /dev/null +++ b/libc-top-half/musl/src/setjmp/wasm32/rt.c @@ -0,0 +1,83 @@ +/* + * a runtime implementation for + * https://github.com/llvm/llvm-project/pull/84137 + * https://docs.google.com/document/d/1ZvTPT36K5jjiedF8MCXbEmYjULJjI723aOAks1IdLLg/edit + */ + +#include +#include + +/* + * function prototypes + */ +void __wasm_setjmp(void *env, uint32_t label, void *func_invocation_id); +uint32_t __wasm_setjmp_test(void *env, void *func_invocation_id); +void __wasm_longjmp(void *env, int val); + +/* + * jmp_buf should have large enough size and alignment to contain + * this structure. + */ +struct jmp_buf_impl { + void *func_invocation_id; + uint32_t label; + + /* + * this is a temorary storage used by the communication between + * __wasm_sjlj_longjmp and WebAssemblyLowerEmscriptenEHSjL-generated + * logic. + * ideally, this can be replaced with multivalue. + */ + struct arg { + void *env; + int val; + } arg; +}; + +void +__wasm_setjmp(void *env, uint32_t label, void *func_invocation_id) +{ + struct jmp_buf_impl *jb = env; + if (label == 0) { /* ABI contract */ + __builtin_trap(); + } + if (func_invocation_id == NULL) { /* sanity check */ + __builtin_trap(); + } + jb->func_invocation_id = func_invocation_id; + jb->label = label; +} + +uint32_t +__wasm_setjmp_test(void *env, void *func_invocation_id) +{ + struct jmp_buf_impl *jb = env; + if (jb->label == 0) { /* ABI contract */ + __builtin_trap(); + } + if (func_invocation_id == NULL) { /* sanity check */ + __builtin_trap(); + } + if (jb->func_invocation_id == func_invocation_id) { + return jb->label; + } + return 0; +} + +void +__wasm_longjmp(void *env, int val) +{ + struct jmp_buf_impl *jb = env; + struct arg *arg = &jb->arg; + /* + * C standard says: + * The longjmp function cannot cause the setjmp macro to return + * the value 0; if val is 0, the setjmp macro returns the value 1. + */ + if (val == 0) { + val = 1; + } + arg->env = env; + arg->val = val; + __builtin_wasm_throw(1, arg); /* 1 == C_LONGJMP */ +}