Skip to content

[lld][WebAssembly] using --import-memory cancels out --page-size=N options without telling user. #146713

Closed
@anlavandier

Description

@anlavandier

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)))

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions