Repository: rust-lang/rust
Pull request: rustc: Stop passing `--allow-undefined` on wasm targets

<!-- homu-ignore:start -->
*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/149868)*
<!-- homu-ignore:end -->

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();
        }
    }

This continues to compile without error and the final wasm binary indeed has an imported function from the host. 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.

<!-- homu-ignore:start -->
<!--
If this PR is related to an unstable feature or an otherwise tracked effort,
please link to the relevant tracking issue here. If you don't know of a related
tracking issue or there are none, feel free to ignore this.

This PR will get automatically assigned to a reviewer. In case you would like
a specific user to review your work, you can assign it to them by using

    r? <reviewer name>
-->
<!-- homu-ignore:end -->


Changed files:
filename: compiler/rustc_target/src/spec/base/wasm.rs
patch: @@ -28,16 +28,6 @@ pub(crate) fn options() -> TargetOptions {
                 // stack overflow will be guaranteed to trap as it underflows instead of
                 // corrupting static data.
                 concat!($prefix, "--stack-first"),
-                // FIXME we probably shouldn't pass this but instead pass an explicit list
-                // of symbols we'll allow to be undefined. We don't currently have a
-                // mechanism of knowing, however, which symbols are intended to be imported
-                // from the environment and which are intended to be imported from other
-                // objects linked elsewhere. This is a coarse approximation but is sure to
-                // hide some bugs and frustrate someone at some point, so we should ideally
-                // work towards a world where we can explicitly list symbols that are
-                // supposed to be imported and have all other symbols generate errors if
-                // they remain undefined.
-                concat!($prefix, "--allow-undefined"),
                 // LLD only implements C++-like demangling, which doesn't match our own
                 // mangling scheme. Tell LLD to not demangle anything and leave it up to
                 // us to demangle these symbols later. Currently rustc does not perform

filename: tests/run-make/wasm-stringify-ints-small/foo.rs
patch: @@ -1,12 +1,14 @@
 #![crate_type = "cdylib"]
 
+#[link(wasm_import_module = "the-world")]
 extern "C" {
     fn observe(ptr: *const u8, len: usize);
 }
 
 macro_rules! s {
     ( $( $f:ident -> $t:ty );* $(;)* ) => {
         $(
+            #[link(wasm_import_module = "the-world")]
             extern "C" {
                 fn $f() -> $t;
             }

filename: tests/run-make/wasm-unexpected-features/foo.rs
patch: @@ -17,6 +17,11 @@ unsafe extern "Rust" {
     fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
 }
 
+#[rustc_std_internal_symbol]
+pub unsafe fn __rdl_alloc_error_handler(_size: usize, _align: usize) -> ! {
+    loop {}
+}
+
 #[used]
 static mut BUF: [u8; 1024] = [0; 1024];
 

filename: tests/ui/linking/executable-no-mangle-strip.rs
patch: @@ -1,5 +1,6 @@
 //@ run-pass
 //@ ignore-windows-gnu: only statics marked with used can be GC-ed on windows-gnu
+//@ ignore-wasm: wasm, for better or worse, exports all #[no_mangle]
 
 // Regression test for <https://github.com/rust-lang/rust/issues/139744>.
 // Functions in the binary marked with no_mangle should be GC-ed if they