Skip to content

wasi libc upgrade#5507

Open
dgryski wants to merge 3 commits into
tinygo-org:devfrom
dgryski:dgryski/wasi-libc-upgrade
Open

wasi libc upgrade#5507
dgryski wants to merge 3 commits into
tinygo-org:devfrom
dgryski:dgryski/wasi-libc-upgrade

Conversation

@dgryski

@dgryski dgryski commented Jul 7, 2026

Copy link
Copy Markdown
Member

No description provided.

dgryski and others added 2 commits July 7, 2026 10:34
wasi-libc migrated its build system from a Makefile to CMake between
wasi-sdk-16 and wasi-sdk-32, restructuring some source directories and
adding new generated headers. Update builder/wasilibc.go to match:

- Generate wasi/version.h (previously produced by CMake's
  configure_file), which defines __wasip1__ and is now widely included
  and used to gate code.
- Follow thread/thrd_sleep.c moving to thread/common/thrd_sleep.c, and
  add newly-added top-half sources (realpath.c, gethostname.c,
  inet_addr.c, inet_legacy.c, inet_ntoa.c).
- Exclude internal/emulate_wait4.c, a new Linux wait4 emulation file
  that needs sys/wait.h, which isn't relevant on WASI.
- Exclude newly-added wasip2/wasip3-only socket sources that now live
  alongside the wasip1 sources in libc-bottom-half/sources, and pick
  up the new libc-bottom-half/sources/math subdirectory.

Bump the wasi-libc cache version to invalidate stale build caches.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wasip2 previously got its libc from a ~1300-line hand-written Go
implementation (src/syscall/libc_wasip2.go) calling wasi:cli/filesystem/io
component bindings directly. wasi-libc upstream already ships a C
implementation of the same wasip2 surface (wasip2.c, wasip2_stdio.c,
descriptor_table.c, socket support), generated from the same WIT
interfaces, so link wasip2 against that instead.

- builder/wasilibc.go: turn libWasiLibc into a newWasiLibc(preview, name)
  factory. libWasiLibcWasip2 builds with __wasip2__ defined and the
  descriptor-table-based socket/stdio sources instead of wasip1's
  cloudlibc syscalls. Distinct library name since the build cache is
  keyed by name+triple and both targets share the same wasm32 triple.
- builder/build.go / targets/wasip2.json: route the "wasi-libc" case to
  the wasip2 variant when the wasip2 build tag is set.
- src/syscall/syscall_libc.go and syscall_libc_wasi.go were already
  shared between wasip1/wasip2 (linker-resolved //export symbols), so
  they needed no changes. env_libc.go/errno_wasilibc.go are now shared
  too; the wasip2-only mini-libc files are deleted.
- Environ() needed to move to wasip1/wasip2-specific files
  (environ_libc.go / environ_wasip2.go): wasi-libc's eager,
  constructor-based environ population is fine for wasip1 (a plain core
  wasm module) but traps under wasip2's component model when it fires
  reentrantly during cabi_realloc ("cannot leave component instance").
  wasip2 uses wasi-libc's documented lazy __wasilibc_get_environ()
  accessor instead.
- runtime_wasip2.go: cabi_realloc now goes through the malloc-tracked
  libc_realloc instead of the GC-internal realloc, since wasi-libc's C
  code (e.g. wasip2_string_free) frees buffers allocated during
  component-ABI lifting with a plain free().
- lib/wasi-libc-wasip2-stub.c: satisfies wasip2.c's forced-link
  reference to wasi-libc's own crt1 component-type metadata, which
  TinyGo doesn't build (it provides its own entry points and its own
  component-embedding step).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dgryski dgryski force-pushed the dgryski/wasi-libc-upgrade branch from c38f864 to 36fb419 Compare July 7, 2026 19:58
lib/wasi-libc-wasip2-stub.c lives outside the wasi-libc submodule (like
lib/picolibc-stdio.c) and was missing from the release recipe's copy
list, so release builds would fail to build wasip2 binaries.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dgryski dgryski requested a review from aykevl July 7, 2026 20:51
Comment on lines +34 to +38
// Use libc_realloc (not the GC-internal realloc) so this allocation is
// tracked in the same allocs map as malloc/free: wasi-libc's own C code
// (e.g. wasip2_string_free) takes ownership of buffers allocated here
// during canonical-ABI lifting and later frees them with a plain free().
return libc_realloc(ptr, uintptr(newsize))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Go types are in the canonical ABI allocated memory, how will these be freed later?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm.. yeah. This is the one Claude change I wasn't certain about. We already have ownership issues with cabi_realloc. It genuinely pointed out an issue, but I'm not convinced this is the correct fix (as opposed to needing to track these allocations elsewhere.) We need to figure out the best way forward (q.v. #4897 )

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, if the cabi_realloc allocated memory holds pointers to Go types, I'm not concerned about them being free'd later, I'm concerned about them accidently being freed now due to not having any other reference. (I'll need to double check that C buffers end up being ignored as "not in heap")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird but this might be half of the right fix—don't make these allocations visible or owned by the GC until the types are handed to the Go code.

I have an old (stale) fix PR for this: #4897

Worth another look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants