emscripten-core / emscripten-core/emscripten

-sSINGLE_FILE: embedded wasm string decodes to 1 extra byte (0x20), producing invalid wasm (large binaries)

Open
#27,600 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
27.6k
Forks
3.6k
Avg merge
1d 1h
Merged PRs (30d)
105

Description

### Version
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 6.0.6 (ce75e06884093bcefb86a6b8fd56a5d62a4cc245)
clang version 24.0.0git (llvm-project ff6d537b14d737719d6377789784d04ff9565f65)
Target: wasm32-unknown-emscripten
Installed via emsdk "latest" on Linux x86_64 (Devuan).

### Problem
Building with `-sSINGLE_FILE` produces an .html whose embedded wasm fails to
instantiate in Firefox 140 ESR:

CompileError: wasm validation error: at offset 8421340: invalid data initializer-kind field

The same project built **without** `-sSINGLE_FILE` produces a .wasm that
`wasm-opt --all-features` validates and that runs correctly in the same browser.

This emcc version embeds the wasm as a raw JS string literal decoded by:

function binaryDecode(f){for(var b,v=0,n=f.length,t=new Uint8Array(n);v>8&b;return t}

(not base64 as older versions did).

### What I found
- Reference (multi-file) wasm: **15,022,632 bytes**, valid.
- Saving the result of `findWasmBinary()` from the browser on the single-file
build gives **15,022,633 bytes** — one extra byte.
- Byte-level comparison: a single `0x20` (space) is present at decoded offset
**8,417,950** that does not exist in the reference wasm. Every byte before it
is identical; every byte after is identical but shifted by one. The validation
error at offset 8,421,340 is the parser failing on the shifted data a few KB later.
- The spurious space physically exists in the emitted .html: inside the embedded
string literal, between a literal `>` character and a `\f` escape sequence.
The surrounding emitted text (escapes shown as written in the file):

2\r%> \f%3

decodes to reference bytes `32 0d 25 3e | 0c 25 33` — the space between `>`
and `\f` corresponds to nothing in the reference binary. So this is an
**encode-side** defect (the emitted string already contains the extra byte),
not a browser/decoding problem.
- Possibly relevant observation: escaping in the emitted string is inconsistent —
e.g. byte 0x0C appears in some places as a raw control character and in other
places as `\f` — which may suggest a post-processing pass (HTML/JS
minification?) rewriting whitespace-class characters inside the string literal
after emission.
- Counter-test: embedding the **identical** wasm bytes as a base64 `data:` URI
in the same .html loads and runs perfectly.
- Size dependence: small programs (hello-world size) built with `-sSINGLE_FILE`
work fine. Two separate large projects fail the same way: a ~15 MB wasm
(extra byte at 8,417,950) and a ~7.5 MB wasm (validation error at 7,490,125).

### Link flags used
-sSINGLE_FILE -sASYNCIFY -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1073741824
-sEXPORTED_RUNTIME_METHODS=stringToNewUTF8 --shell-file -Oz

The C source is generated by the FreeBASIC compiler's emscripten target
(fbc -target js-asmjs), which drives emcc for compile and link.

### Reproduction
Happens consistently for me with multi-MB wasm binaries. The failing .html is
~18 MB — happy to provide it, the matching reference .wasm, and exact byte
offsets on request, or to test candidate fixes / run with EMCC_DEBUG=1.

Contributor guide

Open the contributing guide

Research direction

Start by tracing the -sSINGLE_FILE path that emits the raw JavaScript string, including findWasmBinary() and binaryDecode(), and use EMCC_DEBUG=1 while reproducing with a multi-MB wasm binary. Compare the emitted string and decoded bytes around offset 8,417,950; done means the generated HTML decodes to the same valid bytes as the reference .wasm.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, javascript, wasm
Domain
compilers, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.