From bb1770a08fb9466da22f6b52baf3c50707cec992 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 26 Aug 2026 23:54:24 +0200 Subject: [PATCH] Add support for wasm32-freestanding --- .github/workflows/ci.yml | 1 + build.zig | 24 ++++- builds/Makefile.am | 13 ++- builds/Makefile.in | 13 ++- builds/wasm32-freestanding/include/assert.h | 7 ++ builds/wasm32-freestanding/include/errno.h | 17 ++++ builds/wasm32-freestanding/include/fcntl.h | 3 + builds/wasm32-freestanding/include/stdlib.h | 17 ++++ builds/wasm32-freestanding/include/string.h | 16 ++++ builds/wasm32-freestanding/include/sys/stat.h | 3 + builds/wasm32-freestanding/include/sys/time.h | 11 +++ .../wasm32-freestanding/include/sys/types.h | 8 ++ builds/wasm32-freestanding/include/time.h | 3 + builds/wasm32-freestanding/include/unistd.h | 3 + builds/wasm32-freestanding/libc.c | 94 +++++++++++++++++++ .../crypto_pwhash/argon2/argon2-core.c | 2 +- .../crypto_pwhash/argon2/argon2-encoding.c | 2 +- src/libsodium/crypto_pwhash/argon2/argon2.c | 2 +- .../sodium/crypto_onetimeauth_poly1305.h | 2 +- .../internal/randombytes_internal_random.c | 5 +- .../sysrandom/randombytes_sysrandom.c | 3 +- 21 files changed, 239 insertions(+), 10 deletions(-) create mode 100644 builds/wasm32-freestanding/include/assert.h create mode 100644 builds/wasm32-freestanding/include/errno.h create mode 100644 builds/wasm32-freestanding/include/fcntl.h create mode 100644 builds/wasm32-freestanding/include/stdlib.h create mode 100644 builds/wasm32-freestanding/include/string.h create mode 100644 builds/wasm32-freestanding/include/sys/stat.h create mode 100644 builds/wasm32-freestanding/include/sys/time.h create mode 100644 builds/wasm32-freestanding/include/sys/types.h create mode 100644 builds/wasm32-freestanding/include/time.h create mode 100644 builds/wasm32-freestanding/include/unistd.h create mode 100644 builds/wasm32-freestanding/libc.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fdac9c09..e437d0f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,7 @@ jobs: zig build -Dtarget=x86_64-freebsd zig build -Dtarget=aarch64-freebsd zig build -Dtarget=wasm32-wasi + zig build -Dtarget=wasm32-freestanding zig build -Doptimize=ReleaseFast rm -fr zig-cache zig-out diff --git a/build.zig b/build.zig index b600020d..e2eeb040 100644 --- a/build.zig +++ b/build.zig @@ -10,6 +10,7 @@ const Dir = if (pre_zig17) std.Io.Dir else std.fs.Dir; const Io = if (pre_zig17) std.Io else void; const has_build_root_handle = @hasField(std.Build, "build_root"); +const wasm_libc_path = "builds/wasm32-freestanding"; fn initLibConfig(b: *std.Build, target: std.Build.ResolvedTarget, lib: *Compile) void { lib.root_module.link_libc = true; @@ -103,6 +104,12 @@ fn initLibConfig(b: *std.Build, target: std.Build.ResolvedTarget, lib: *Compile) lib.root_module.addCMacro("HAVE_SYS_PARAM_H", "1"); lib.root_module.addCMacro("HAVE_SYS_RANDOM_H", "1"); }, + .freestanding => { + if (target.result.cpu.arch.isWasm()) { + lib.root_module.link_libc = false; + lib.root_module.addSystemIncludePath(b.path(wasm_libc_path ++ "/include")); + } + }, .freebsd => { lib.root_module.addCMacro("ASM_HIDE_SYMBOL", ".hidden"); lib.root_module.addCMacro("TLS", "_Thread_local"); @@ -197,11 +204,19 @@ pub fn build(b: *std.Build) !void { var build_static = b.option(bool, "static", "Build libsodium as a static library.") orelse true; var build_shared = b.option(bool, "shared", "Build libsodium as a shared library.") orelse true; - const build_tests = b.option(bool, "test", "Build the tests (implies -Dstatic=true)") orelse true; + var build_tests = b.option(bool, "test", "Build the tests (implies -Dstatic=true)") orelse true; + + const wasm_freestanding = + target.result.cpu.arch.isWasm() and target.result.os.tag == .freestanding; if (target.result.cpu.arch.isWasm()) { build_shared = false; } + // The test programs need a hosted environment: standard input and output, + // and files to read the expected results from. + if (wasm_freestanding) { + build_tests = false; + } if (build_tests) { build_static = true; } @@ -268,6 +283,13 @@ pub fn build(b: *std.Build) !void { "-Werror=vla", }; + if (wasm_freestanding) { + lib.root_module.addCSourceFiles(.{ + .files = &.{wasm_libc_path ++ "/libc.c"}, + .flags = flags, + }); + } + const allocator = heap.page_allocator; var walker = try src_dir.walk(allocator); diff --git a/builds/Makefile.am b/builds/Makefile.am index e0e82ec4..625c1c1c 100644 --- a/builds/Makefile.am +++ b/builds/Makefile.am @@ -84,4 +84,15 @@ EXTRA_DIST = \ msvc/vs2026/libsodium/libsodium.xml \ msvc/vs2026/libsodium.import.props \ msvc/vs2026/libsodium.import.xml \ - msvc/vs2026/libsodium.sln + msvc/vs2026/libsodium.sln \ + wasm32-freestanding/libc.c \ + wasm32-freestanding/include/assert.h \ + wasm32-freestanding/include/errno.h \ + wasm32-freestanding/include/fcntl.h \ + wasm32-freestanding/include/stdlib.h \ + wasm32-freestanding/include/string.h \ + wasm32-freestanding/include/time.h \ + wasm32-freestanding/include/unistd.h \ + wasm32-freestanding/include/sys/stat.h \ + wasm32-freestanding/include/sys/time.h \ + wasm32-freestanding/include/sys/types.h diff --git a/builds/Makefile.in b/builds/Makefile.in index 2679d0a0..859bb958 100644 --- a/builds/Makefile.in +++ b/builds/Makefile.in @@ -389,7 +389,18 @@ EXTRA_DIST = \ msvc/vs2026/libsodium/libsodium.xml \ msvc/vs2026/libsodium.import.props \ msvc/vs2026/libsodium.import.xml \ - msvc/vs2026/libsodium.sln + msvc/vs2026/libsodium.sln \ + wasm32-freestanding/libc.c \ + wasm32-freestanding/include/assert.h \ + wasm32-freestanding/include/errno.h \ + wasm32-freestanding/include/fcntl.h \ + wasm32-freestanding/include/stdlib.h \ + wasm32-freestanding/include/string.h \ + wasm32-freestanding/include/time.h \ + wasm32-freestanding/include/unistd.h \ + wasm32-freestanding/include/sys/stat.h \ + wasm32-freestanding/include/sys/time.h \ + wasm32-freestanding/include/sys/types.h all: all-am diff --git a/builds/wasm32-freestanding/include/assert.h b/builds/wasm32-freestanding/include/assert.h new file mode 100644 index 00000000..9e4e33c2 --- /dev/null +++ b/builds/wasm32-freestanding/include/assert.h @@ -0,0 +1,7 @@ +#undef assert + +#ifdef NDEBUG +#define assert(cond) ((void)0) +#else +#define assert(cond) ((cond) ? (void)0 : __builtin_trap()) +#endif diff --git a/builds/wasm32-freestanding/include/errno.h b/builds/wasm32-freestanding/include/errno.h new file mode 100644 index 00000000..96c0aed3 --- /dev/null +++ b/builds/wasm32-freestanding/include/errno.h @@ -0,0 +1,17 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +extern int errno; + +#define EPERM 1 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define EAGAIN 11 +#define ENOMEM 12 +#define EINVAL 22 +#define EFBIG 27 +#define ERANGE 34 +#define ENOSYS 38 + +#endif diff --git a/builds/wasm32-freestanding/include/fcntl.h b/builds/wasm32-freestanding/include/fcntl.h new file mode 100644 index 00000000..edbb3bbf --- /dev/null +++ b/builds/wasm32-freestanding/include/fcntl.h @@ -0,0 +1,3 @@ +#ifndef _FCNTL_H +#define _FCNTL_H +#endif diff --git a/builds/wasm32-freestanding/include/stdlib.h b/builds/wasm32-freestanding/include/stdlib.h new file mode 100644 index 00000000..7eab1601 --- /dev/null +++ b/builds/wasm32-freestanding/include/stdlib.h @@ -0,0 +1,17 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include +#include + +/* arc4random_buf, malloc and free come from the embedder. */ + +_Noreturn void abort(void); + +uint32_t arc4random(void); +void arc4random_buf(void *buf, size_t size); +void *calloc(size_t nmemb, size_t size); +void free(void *ptr); +void *malloc(size_t size); + +#endif diff --git a/builds/wasm32-freestanding/include/string.h b/builds/wasm32-freestanding/include/string.h new file mode 100644 index 00000000..5ddb2cb2 --- /dev/null +++ b/builds/wasm32-freestanding/include/string.h @@ -0,0 +1,16 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + +void *memchr(const void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *strchr(const char *s, int c); +size_t strlen(const char *s); +int strncmp(const char *s1, const char *s2, size_t n); +char *strrchr(const char *s, int c); + +#endif diff --git a/builds/wasm32-freestanding/include/sys/stat.h b/builds/wasm32-freestanding/include/sys/stat.h new file mode 100644 index 00000000..346aedf7 --- /dev/null +++ b/builds/wasm32-freestanding/include/sys/stat.h @@ -0,0 +1,3 @@ +#ifndef _SYS_STAT_H +#define _SYS_STAT_H +#endif diff --git a/builds/wasm32-freestanding/include/sys/time.h b/builds/wasm32-freestanding/include/sys/time.h new file mode 100644 index 00000000..258db8ec --- /dev/null +++ b/builds/wasm32-freestanding/include/sys/time.h @@ -0,0 +1,11 @@ +#ifndef _SYS_TIME_H +#define _SYS_TIME_H + +struct timeval { + long long tv_sec; + long tv_usec; +}; + +int gettimeofday(struct timeval *tv, void *tz); + +#endif diff --git a/builds/wasm32-freestanding/include/sys/types.h b/builds/wasm32-freestanding/include/sys/types.h new file mode 100644 index 00000000..c13f37a4 --- /dev/null +++ b/builds/wasm32-freestanding/include/sys/types.h @@ -0,0 +1,8 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H + +#include + +typedef ptrdiff_t ssize_t; + +#endif diff --git a/builds/wasm32-freestanding/include/time.h b/builds/wasm32-freestanding/include/time.h new file mode 100644 index 00000000..446b6f26 --- /dev/null +++ b/builds/wasm32-freestanding/include/time.h @@ -0,0 +1,3 @@ +#ifndef _TIME_H +#define _TIME_H +#endif diff --git a/builds/wasm32-freestanding/include/unistd.h b/builds/wasm32-freestanding/include/unistd.h new file mode 100644 index 00000000..0a2c7b9b --- /dev/null +++ b/builds/wasm32-freestanding/include/unistd.h @@ -0,0 +1,3 @@ +#ifndef _UNISTD_H +#define _UNISTD_H +#endif diff --git a/builds/wasm32-freestanding/libc.c b/builds/wasm32-freestanding/libc.c new file mode 100644 index 00000000..863c070a --- /dev/null +++ b/builds/wasm32-freestanding/libc.c @@ -0,0 +1,94 @@ +#include +#include + +#include +#include +#include + +int errno; + +void *calloc(size_t nmemb, size_t size) { + void *ptr; + + if (nmemb > (size_t)0U && size > SIZE_MAX / nmemb) { + return NULL; + } + if ((ptr = malloc(nmemb * size)) != NULL) { + memset(ptr, 0, nmemb * size); + } + return ptr; +} + +_Noreturn void abort(void) { + __builtin_trap(); +} + +uint32_t arc4random(void) { + uint32_t r; + + arc4random_buf(&r, sizeof r); + + return r; +} + +void *memchr(const void *s, int c, size_t n) { + const unsigned char *p = (const unsigned char *)s; + + while (n-- > (size_t)0U) { + if (*p == (unsigned char) c) { + return (void *) (uintptr_t) p; + } + p++; + } + return NULL; +} + +size_t strlen(const char *s) { + const char *p = s; + + while (*p != 0) { + p++; + } + return (size_t)(p - s); +} + +int strncmp(const char *s1, const char *s2, size_t n) { + while (n-- > (size_t) 0U) { + if (*s1 != *s2) { + return (int) (unsigned char) *s1 - (int) (unsigned char) *s2; + } + if (*s1 == 0) { + break; + } + s1++; + s2++; + } + return 0; +} + +char *strchr(const char *s, int c) { + for (;;) { + if (*s == (char) c) { + return (char *) (uintptr_t) s; + } + if (*s == 0) { + return NULL; + } + s++; + } +} + +char *strrchr(const char *s, int c) { + const char *found = NULL; + + for (;;) { + if (*s == (char) c) { + found = s; + } + if (*s == 0) { + break; + } + s++; + } + return (char *) (uintptr_t) found; +} diff --git a/src/libsodium/crypto_pwhash/argon2/argon2-core.c b/src/libsodium/crypto_pwhash/argon2/argon2-core.c index d1274ba3..ff0e52c5 100644 --- a/src/libsodium/crypto_pwhash/argon2/argon2-core.c +++ b/src/libsodium/crypto_pwhash/argon2/argon2-core.c @@ -12,8 +12,8 @@ */ #include +#include #include -#include #include #include diff --git a/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c b/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c index 3c9b4927..1ba30368 100644 --- a/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c +++ b/src/libsodium/crypto_pwhash/argon2/argon2-encoding.c @@ -2,7 +2,7 @@ #include "argon2-core.h" #include "utils.h" #include -#include +#include #include #include diff --git a/src/libsodium/crypto_pwhash/argon2/argon2.c b/src/libsodium/crypto_pwhash/argon2/argon2.c index ddc55c2c..1706e993 100644 --- a/src/libsodium/crypto_pwhash/argon2/argon2.c +++ b/src/libsodium/crypto_pwhash/argon2/argon2.c @@ -12,8 +12,8 @@ */ #include +#include #include -#include #include #include diff --git a/src/libsodium/include/sodium/crypto_onetimeauth_poly1305.h b/src/libsodium/include/sodium/crypto_onetimeauth_poly1305.h index 1565d37a..54e556d1 100644 --- a/src/libsodium/include/sodium/crypto_onetimeauth_poly1305.h +++ b/src/libsodium/include/sodium/crypto_onetimeauth_poly1305.h @@ -1,8 +1,8 @@ #ifndef crypto_onetimeauth_poly1305_H #define crypto_onetimeauth_poly1305_H +#include #include -#include #include #include diff --git a/src/libsodium/randombytes/internal/randombytes_internal_random.c b/src/libsodium/randombytes/internal/randombytes_internal_random.c index 33c04ad6..3059f24b 100644 --- a/src/libsodium/randombytes/internal/randombytes_internal_random.c +++ b/src/libsodium/randombytes/internal/randombytes_internal_random.c @@ -73,7 +73,8 @@ BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); #define INTERNAL_RANDOM_BLOCK_SIZE crypto_core_hchacha20_OUTPUTBYTES -#if defined(__OpenBSD__) || defined(__CloudABI__) || defined(__wasi__) +#if defined(__OpenBSD__) || defined(__CloudABI__) || \ + (defined(__wasm__) && !defined(__EMSCRIPTEN__)) # define HAVE_SAFE_ARC4RANDOM 1 #endif #if defined(__CloudABI__) || defined(__wasm__) @@ -503,7 +504,7 @@ randombytes_internal_random_close(void) if (global.getrandom_available != 0) { ret = 0; } -# elif !defined(NONEXISTENT_DEV_RANDOM) && defined(HAVE_SAFE_ARC4RANDOM) +# elif defined(HAVE_SAFE_ARC4RANDOM) ret = 0; # else if (global.random_data_source_fd != -1 && diff --git a/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c b/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c index ed1ebee0..b469fa73 100644 --- a/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c +++ b/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c @@ -75,7 +75,8 @@ BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); # pragma comment(lib, "advapi32.lib") #endif -#if defined(__OpenBSD__) || defined(__CloudABI__) || defined(__wasi__) +#if defined(__OpenBSD__) || defined(__CloudABI__) || \ + (defined(__wasm__) && !defined(__EMSCRIPTEN__)) # define HAVE_SAFE_ARC4RANDOM 1 #endif