Description
The current version of the Webassembly linker wasm-ld
allows the argument --page-size=N
with N being either 1
or 65536
since the merging of #128942. For some reason, when also passing the --import-memory
flag, the page-size
is completely disregarded without telling the caller. This results in .wasm
files that try to import a memory with the default page-size even if --page-size=1
was given.
Edit: After realizing that I'm an idiot and that wabt
's wasm2wat
does support the custom-page-sizes
proposal by simply passing a flag to it, I edited the issue accordingly to make it more understandable
How to reproduce
I don't know anything about linkers and discovered that while compiling rust code to wasm bytecode so it's what I'll describe.
Setup :
rustc -Vv
rustc 1.88.0 (6b00bc388 2025-06-23)
binary: rustc
commit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc
commit-date: 2025-06-23
host: x86_64-unknown-linux-gnu
release: 1.88.0
LLVM version: 20.1.5
wasm-ld --version
LLD 21.0.0 (https://github.com/llvm/llvm-project.git 1b7cbe1f871ebe572e383a2531257b88168eea1b) # Compiled from source on 2.07.25
wasm2wat --version
1.0.37 (git~1.0.37-27-ga55fb946)
cargo new --lib wasm-ld-bug-test
cd wasm-ld-bug-test
add
[lib]
crate-type=["cdylib", "rlib]
to the Cargo.toml
. Then change src/lib.rs
to
#![no_std]
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! {
core::arch::wasm32::unreachable()
}
Now, if you compile this with
cargo rustc --target wasm32v1-none -- -Clinker=wasm-ld -Clink-arg=--page-size=1
You can see that the compiled uses the custom-page-size
proposal by trying to convert it to .wat
wasm2wat --enable-custom-page-sizes target/wasm32v1/debug/wasm_ld_bug_test.wasm
(module $wasm_ld_bug_test.wasm
(table (;0;) 1 1 funcref)
(memory (;0;) 1048576 (pagesize 1))
(global $__stack_pointer (mut i32) (i32.const 1048576))
(global (;1;) i32 (i32.const 1048576))
(global (;2;) i32 (i32.const 1048576))
(export "memory" (memory 0))
(export "__data_end" (global 1))
(export "__heap_base" (global 2)))
Then by recompiling it while also using --import-memory
you get
cargo rustc --target wasm32v1-none -- -Clinker=wasm-ld -Clink-arg=--page-size=1 -Clink-arg=--import-memory
wasm2wat target/wasm32v1/debug/wasm_ld_bug_test.wasm
target/wasm32v1-none/debug/wasm_ld_bug_test.wasm:000001b: error: initial pages (1048576) must be <= (65536)
This happens because by default, the linker sets the stack size
to 1048576
bytes and since the memory pages are of size 1 (at least before being actually written to the wasm
files), it asks for 1048576
pages of memory. This didn't happen in the previous case despite the fact that the file does also ask for 1048576
pages.
By changing the stack-size
to something more manageable we can see that the pagesize
variable for the memory has simply been removed.
cargo rustc --target wasm32v1-none -- -Clinker=wasm-ld -Clink-arg=-zstack-size=1024 -Clink-arg=--page-size=1 -Clink-arg=-
-import-memory
wasm2wat -enable-custom-page-sizes target/wasm32v1/debug/wasm_ld_bug_test.wasm
(module $wasm_ld_bug_test.wasm
(import "env" "memory" (memory (;0;) 1024))
(table (;0;) 1 1 funcref)
(global $__stack_pointer (mut i32) (i32.const 1024))
(global (;1;) i32 (i32.const 1024))
(global (;2;) i32 (i32.const 1024))
(export "__data_end" (global 1))
(export "__heap_base" (global 2)))