GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Emscripten: static (non-MAIN_MODULE) builds fail at startup on dynamic-linking-only JS helpers · Issue #156780 · python/cpython

Emscripten: static (non-MAIN_MODULE) builds fail at startup on dynamic-linking-only JS helpers #156780

New issue
Copy link
New issue
Copy link
Closed
#156781
Closed
Emscripten: static (non-MAIN_MODULE) builds fail at startup on dynamic-linking-only JS helpers#156780
#156781
Copy link
Labels
OS-emscriptenbuildThe build process and cross-buildThe build process and cross-buildtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@clementperon
clementperon
opened on Sep 1, 2026
Issue body actions

Bug description:

CPython's Emscripten platform code is written for the -sMAIN_MODULE dynamic-linking configuration it is built and tested in. Several EM_JS bodies reference JS helpers that only exist in that configuration. Because EM_JS bodies are emitted verbatim into the output, Emscripten's dependency tracker cannot see those references, so a static link silently emits JS calling helpers it never included and fails at runtime instead of at link time.

Four such references, each confirmed against Emscripten 6.0.9:

  1. Python/emscripten_syscalls.c:110 hooks resolveGlobalSymbol() to wrap main() in WebAssembly.promising(). That helper is defined in src/lib/libdylink.js, which src/modules.mjs:99 links in only if (MAIN_MODULE); the file itself #errors otherwise, and the symbol is marked __internal: true. The read sits below the if (!WebAssembly.promising) return; guard on line 106, so this only fires once a runtime exposes JSPI.
  2. Python/emscripten_trampoline.c:54 and :82 use wasmTable and addFunction(). Neither is included by default; MAIN_MODULE happens to pull both in through libdylink's own __deps.
  3. The promising-main wrapper calls _emscripten_exit_with_live_runtime() (emscripten_syscalls.c:128), which is likewise dropped from a static link.
  4. Python/emscripten_trampoline.c:93 reads __PyRuntime, which is bound into the JS glue only because configure lists it in -sEXPORTED_FUNCTIONS for CPython's own python.mjs link. An embedder linking libpython into their own application gets no such flag. Unlike the others, this one fails on every runtime, JSPI or not.

Minimal reproducer

This is the shape of (1) with no CPython involved:

#include <emscripten.h>
EM_JS(void, probe, (void), {
    if (!WebAssembly.promising) return;
    const orig = resolveGlobalSymbol;
})
__attribute__((constructor)) void c(void) { probe(); }
int main(void) { return 0; }
$ emcc t.c -o t.js          # links clean, no diagnostic at all
$ node t.js                 # fine, the guard returns early
$ node --experimental-wasm-jspi t.js
ReferenceError: resolveGlobalSymbol is not defined
    at probe (t.js:1513:136)
    at initRuntime (t.js:540:35)

Emscripten does warn for library symbols it knows about — referencing addFunction the same way produces "addFunction is a library symbol and not included by default". resolveGlobalSymbol gets nothing, because without MAIN_MODULE libdylink.js is never loaded and the name is not in the symbol set to warn about.

Full reproduction

I built libpython3.14.a for wasm32-emscripten with --disable-wasm-dynamic-linking MODULE_BUILDTYPE=static CFLAGS=-DPY_CALL_TRAMPOLINE and linked it into a small C application that runs an interactive Python console in the browser. It aborts during startup on __PyRuntime; with that symbol exported by hand it then aborts on resolveGlobalSymbol as soon as JSPI is enabled, and works with JSPI off.

Note on the fix

EM_JS_DEPS (emscripten/em_macros.h) exists precisely to declare these, and emscripten_syscalls.c:43 already uses it for $SYSCALLS. Declaring $resolveGlobalSymbol converts the silent breakage into a link-time error: undefined symbol: $resolveGlobalSymbol, which is the behaviour you would want in a configuration that genuinely cannot support it.

Why this is not caught today

Every wasm test builds through Platforms/emscripten/__main__.py, which hard-codes --enable-wasm-dynamic-linking at line 510. web_example, web_example_pyrepl_jspi and browser_test therefore all exercise MAIN_MODULE only; the static configuration has no coverage.

Context for the report: this blocks embedding CPython in Kodi's WebAssembly port, which links statically.

CPython versions tested on:

3.14, CPython main branch

Operating systems tested on:

Other

Linked PRs

  • gh-156780: Emscripten: support building CPython with static linking #156781
  • gh-156780: Emscripten: add missing EM_JS_DEPS #156798
  • gh-156780: Emscripten: drop the trampoline's dependence on exports #157238

Activity

Next
  1. ZeroIntensity
    added
    OS-emscripten
    on Sep 1, 2026
  2. StanFromIreland
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    buildThe build process and cross-buildThe build process and cross-build
    on Sep 1, 2026
  3. clementperon
    added 12 commits that reference this issue on Sep 1, 2026

    pythongh-156780: Emscripten: move promising-main setup out of libpython

    5a45a5d

    pythongh-156780: Emscripten: support static linking

    1b00df2

    pythongh-156780: Emscripten: add an embedding smoke test

    c89eb2f

    pythongh-156780: Emscripten: add missing EM_JS_DEPS

    692abd8

    pythongh-156780: Emscripten: move promising-main setup out of libpython

    3916b1d

    pythongh-156780: Emscripten: support static linking

    12a6bbc

    pythongh-156780: Emscripten: add an embedding smoke test

    2e46f9a

    pythongh-156780: Emscripten: add missing EM_JS_DEPS

    f770de5

    pythongh-156780: Emscripten: drop the duplicate sysmodule EM_JS_DEPS

    8015a97

    pythongh-156780: Emscripten: drop the duplicate sysmodule EM_JS_DEPS

    55cdf89

    pythongh-156780: Emscripten: gate the suspending syscalls on the entr…

    8a2da5b

    pythongh-156780: Simplify the NEWS entry

    fa3261b
  4. 30 remaining items

  5. freakboy3742
    added a commit that references this issue on Sep 11, 2026

    gh-156780: Emscripten: drop the trampoline's dependence on exports (#…

    d0b2e07
  6. clin1234
    added 2 commits that reference this issue on Sep 12, 2026

    pythongh-156780: Emscripten: add missing EM_JS_DEPS (python#156798)

    c3896d2

    pythongh-156780: Emscripten: drop the trampoline's dependence on expo…

    2d83f1a
  7. johnslavik
    added 2 commits that reference this issue on Sep 14, 2026

    pythongh-156780: Emscripten: add missing EM_JS_DEPS (python#156798)

    878d8f0

    pythongh-156780: Emscripten: drop the trampoline's dependence on expo…

    bebac0e
  8. clementperon
    added 3 commits that reference this issue on Sep 16, 2026

    pythongh-156780: Emscripten: move main() wrapping out of libpython

    e8851a4

    pythongh-156780: Emscripten: gate the suspending syscalls on the main…

    b60c4a0

    pythongh-156780: Emscripten: add an embedding smoke test

    afd1a38
  9. clementperon
    added 5 commits that reference this issue on Oct 19, 2026

    pythongh-156780: Emscripten: move main() wrapping out of libpython

    bf034a6

    pythongh-156780: Emscripten: gate the suspending syscalls on the main…

    9a86800

    pythongh-156780: Emscripten: add an embedding smoke test

    f0f808e

    pythongh-156780: preserve Emscripten main wrapper behavior

    09b9faf

    pythongh-156780: Emscripten: drop the promising_main test

    84b96aa
  10. freakboy3742
    added a commit that references this issue on Oct 22, 2026

    gh-156780: Emscripten: support building CPython with static linking (#…

    d6b77c8
  11. freakboy3742
    closed this as completedin #156781on Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    OS-emscriptenbuildThe build process and cross-buildThe build process and cross-buildtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.