Skip to content

Conversation

@alexcrichton
Copy link
Member

@alexcrichton alexcrichton commented Dec 10, 2025

This commit updates how the linker is invoked on WebAssembly targets (all of them) to avoid passing the --allow-undefined flag to the linker. Historically, if I remember this correctly, when wasm-ld was first integrated this was practically required because at the time it was otherwise impossible to import a function from the host into a wasm binary. Or, at least, I'm pretty sure that was why this was added.

At the time, as the documentation around this option indicates, it was known that this was going to be a hazard. This doesn't match behavior on native, for example, and can easily paper over what should be a linker error with some sort of other obscure runtime error. An example is that this program currently compiles and links, it just prints null:

unsafe extern "C" {
    static nonexistent: u8;
}

fn main() {
    println!("{:?}", &raw const nonexistent);
}

This can easily lead to mistakes like rust-lang/libc#4880 and defer what should be a compile-time link error to weird or unusual behavior at link time. Additionally, in the intervening time since wasm-ld was first introduced here, lots has changed and notably this program works as expected:

#[link(wasm_import_module = "host")]
unsafe extern "C" {
    fn foo();
}

fn main() {
    unsafe {
        foo();
    }
}

Notably this continues to compile without error and the final wasm binary indeed has an imported function from the host. What this change means, however, is that this program:

unsafe extern "C" {
    fn foo();
}

fn main() {
    unsafe {
        foo();
    }
}

this currently compiles successfully and emits an import from the env module. After this change, however, this will fail to compile with a link error stating that the foo symbol is not defined.

This commit updates how the linker is invoked on WebAssembly targets
(all of them) to avoid passing the `--allow-undefined` flag to the
linker. Historically, if I remember this correctly, when `wasm-ld` was
first integrated this was practically required because at the time it
was otherwise impossible to import a function from the host into a wasm
binary. Or, at least, I'm pretty sure that was why this was added.

At the time, as the documentation around this option indicates, it was
known that this was going to be a hazard. This doesn't match behavior on
native, for example, and can easily paper over what should be a linker
error with some sort of other obscure runtime error. An example is that
this program currently compiles and links, it just prints null:

    unsafe extern "C" {
        static nonexistent: u8;
    }

    fn main() {
        println!("{:?}", &raw const nonexistent);
    }

This can easily lead to mistakes like rust-lang/libc/4880 and defer what
should be a compile-time link error to weird or unusual behavior at link
time. Additionally, in the intervening time since `wasm-ld` was first
introduced here, lots has changed and notably this program works as
expected:

    #[link(wasm_import_module = "host")]
    unsafe extern "C" {
        fn foo();
    }

    fn main() {
        unsafe {
            foo();
        }
    }

Notably this continues to compile without error and the final wasm
binary indeed has an imported function from the host. What this change
means, however, is that this program:

    unsafe extern "C" {
        fn foo();
    }

    fn main() {
        unsafe {
            foo();
        }
    }

this currently compiles successfully and emits an import from the `env`
module. After this change, however, this will fail to compile with a
link error stating that the `foo` symbol is not defined.
@rustbot
Copy link
Collaborator

rustbot commented Dec 10, 2025

These commits modify compiler targets.
(See the Target Tier Policy.)

@rustbot rustbot added A-run-make Area: port run-make Makefiles to rmake.rs S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Dec 10, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 10, 2025

r? @jackh726

rustbot has assigned @jackh726.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@alexcrichton
Copy link
Member Author

I'll clarify that process-wise I'm not exactly sure how to approach this. This is highly likely to break projects in practice but I don't know how to evaluate the quantity or breadth of breakage. If it's possible to do a wasm crater run or something like that I'd be happy to triage the results and/or send PRs to upstream projects.

In the meantime though I wanted to get this on the radar as I should have done this ~years ago and it's long overdue that this change is made for wasm (my fault)

@Mark-Simulacrum
Copy link
Member

It should be possible to run Crater with a custom target (https://github.com/rust-lang/crater/blob/master/docs/bot-usage.md#specifying-toolchains). I'll kick off a try job with the right artifacts (I think) at least. I'm not sure how much that will actually be helpful though in terms of what % of crates are actually supposed to build and do build for wasm32.

One thought is that we could possibly phase this in for at least the wasi targets with the next preview (p4?) assuming that is coming down the line, or in theory with an edition of the leaf crate, but neither feel like amazing options.

One other thought is that we could maybe FCW lint if we run the linker without --allow-undefined and if that fails re-run again with it, and if that works then emit a lint? Not sure how reliable the failure is.

@bors try jobs=dist-x86_64-linux,dist-various-*

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Dec 11, 2025
rustc: Stop passing `--allow-undefined` on wasm targets

try-job: dist-x86_64-linux
try-job: dist-various-*
@rust-bors
Copy link

rust-bors bot commented Dec 11, 2025

☀️ Try build successful (CI)
Build commit: 23647e6 (23647e694de8d0904848ad068b2e0ec2dd098c37, parent: f5209000832c9d3bc29c91f4daef4ca9f28dc797)

@alexcrichton
Copy link
Member Author

Oh nice! I'll attempt that here and see how it goes...

@craterbot run start=master#f5209000832c9d3bc29c91f4daef4ca9f28dc797+target=wasm32-wasip1 end=try#23647e694de8d0904848ad068b2e0ec2dd098c37+target=wasm32-wasip1 mode=build-only

One thought is that we could possibly phase this in for at least the wasi targets with the next preview ...

One other thought is that we could maybe FCW lint if we run the linker without --allow-undefined and if that fails re-run again with it, and if that works then emit a lint?

Agreed! I figure we can await the crater results and see if that provides a good signal of which path to take here, as I don't feel I have a great gut feeling myself here.

@craterbot
Copy link
Collaborator

👌 Experiment pr-149868 created and queued.
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Dec 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-run-make Area: port run-make Makefiles to rmake.rs S-waiting-on-crater Status: Waiting on a crater run to be completed. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants