Repository: bytecodealliance/wasmtime (rust)

Issue:
Wasmtime: missing trampolines for declarative element segments (should be treated as possibly exported).
### Test Case

Repro to run with `main`:

```rust
use wasmtime::*;

fn main() {
    let engine = Engine::default();

    let module = Module::new(
        &engine,
        r#"(module
        (import "" "callback" (func $callback (param funcref)))
        (elem declare func $f) 
        (func $f (param externref))
        (func (export "g")
          (call $callback (ref.func $f))
        )
      )"#,
    )
    .unwrap();

    let store = Store::new(&engine);

    let callback = Func::wrap(&store, |funcref: Option<Func>| {
        funcref
            .unwrap()
            .typed::<Option<ExternRef>, ()>()
            .unwrap()
            .call(Some(ExternRef::new("some data")))
            .unwrap();
    });

    let instance = Instance::new(&store, &module, &[callback.into()]).unwrap();

    let g = instance
        .get_typed_func::<(), ()>("g")
        .unwrap();

    g.call(()).unwrap();

    println!("success!");
}
```

This is a paired down test case that is currently causing the `wasmtime-dotnet` CI to fail.

### Steps to Reproduce

On Windows:

* `cargo new --bin repro`
* `cargo add wasmtime` (change path to local wasmtime repo)
* `cargo run`

### Expected Results

`success!` should be printed.

### Actual Results

Panic due to failure to find trampoline (for signature `(externref) -> ()`, i.e. the funcref passed to `callback`):

```text
thread 'main' panicked at 'trampoline missing', C:\Users\Peter\src\wasmtime\crates\wasmtime\src\store.rs:272:14
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/2fd73fabe469357a12c2c974c140f67e7cdd76d0\/library\std\src\panicking.rs:493
   1: core::panicking::panic_fmt
             at /rustc/2fd73fabe469357a12c2c974c140f67e7cdd76d0\/library\core\src\panicking.rs:92
   2: core::option::expect_failed
             at /rustc/2fd73fabe469357a12c2c974c140f67e7cdd76d0\/library\core\src\option.rs:1292
   3: core::option::Option<unsafe extern "C" fn(mut wasmtime_runtime::vmcontext::VMContext*, mut wasmtime_runtime::vmcontext::VMContext*, const wasmtime_runtime::vmcontext::VMFunctionBody*, mut u128*)>::expect<unsafe extern "C" fn(mut wasmtime_runtime::vmcontex
             at C:\Users\Peter\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:349
   4: wasmtime::store::Store::lookup_trampoline
             at C:\Users\Peter\src\wasmtime\crates\wasmtime\src\store.rs:268
   5: wasmtime::func::Func::from_wasmtime_function
             at C:\Users\Peter\src\wasmtime\crates\wasmtime\src\func.rs:919
   6: wasmtime::func::Func::from_caller_checked_anyfunc
             at C:\Users\Peter\src\wasmtime\crates\wasmtime\src\func.rs:551
   7: wasmtime::func::typed::{{impl}}::from_abi
...
```

### Versions and Environment

Wasmtime version or commit: `main`

Operating system: Windows 10

Architecture: x86-64

### Extra Info

I believe this is caused by #2806.

I think the problem here is that we're failing to determine that `$f` may be possibly exported from the module as the element segment is declarative and not active or passive.  Right now the section translator does nothing for declarative element segments; with the changes from #2806 we should consider all declarative elements as potentially exported functions as the reference might escape the module.

_Note: by changing `$f` to have a signature of `() -> ()`, it runs to completion because it uses the trampoline of `g`._



Pull request description:
Process declared element segments for "possibly exported funcs"
Now that we're using "possibly exported" as an impactful decision for
codegen (which trampolines to generate and which ABI a function has)
it's important that we calculate this property of a wasm function
correctly! Previously Wasmtime forgot to processed "declared" elements
in apart from active/passive element segments, but this updates Wasmtime
to ensure that these entries are processed and all the functions
contained within are flagged as "possibly exported".

Closes #2850

<!--

Please ensure that the following steps are all taken care of before submitting
the PR.

- [ ] This has been discussed in issue #..., or if not, please tell us why
  here.
- [ ] A short description of what this does, why it is needed; if the
  description becomes long, the matter should probably be discussed in an issue
  first.
- [ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
  If you don't know who could review this, please indicate so. The list of
  suggested reviewers on the right can help you.

Please ensure all communication adheres to the [code of
conduct](https://github.com/bytecodealliance/wasmtime/blob/master/CODE_OF_CONDUCT.md).
-->


Patch:
diff --git a/cranelift/wasm/src/environ/spec.rs b/cranelift/wasm/src/environ/spec.rs
index ab7974f57..31c8d86a4 100644
--- a/cranelift/wasm/src/environ/spec.rs
+++ b/cranelift/wasm/src/environ/spec.rs
@@ -948,6 +948,13 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment {
         elements: Box<[FuncIndex]>,
     ) -> WasmResult<()>;
 
+    /// Indicates that a declarative element segment was seen in the wasm
+    /// module.
+    fn declare_elements(&mut self, elements: Box<[FuncIndex]>) -> WasmResult<()> {
+        drop(elements);
+        Ok(())
+    }
+
     /// Provides the number of passive data segments up front.
     ///
     /// By default this does nothing, but implementations may use this to
diff --git a/cranelift/wasm/src/sections_translator.rs b/cranelift/wasm/src/sections_translator.rs
index 3ce00e61d..bd4dcd113 100644
--- a/cranelift/wasm/src/sections_translator.rs
+++ b/cranelift/wasm/src/sections_translator.rs
@@ -400,7 +400,7 @@ pub fn parse_element_section<'data>(
                 environ.declare_passive_element(index, segments)?;
             }
             ElementKind::Declared => {
-                // Nothing to do here.
+                environ.declare_elements(segments)?;
             }
         }
     }
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index 393a83975..324c4b929 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -746,6 +746,13 @@ impl<'data> cranelift_wasm::ModuleEnvironment<'data> for ModuleEnvironment<'data
         Ok(())
     }
 
+    fn declare_elements(&mut self, segments: Box<[FuncIndex]>) -> WasmResult<()> {
+        for element in segments.iter() {
+            self.flag_func_possibly_exported(*element);
+        }
+        Ok(())
+    }
+
     fn reserve_function_bodies(&mut self, _count: u32, offset: u64) {
         self.result.debuginfo.wasm_file.code_section_offset = offset;
     }


Test patch:
diff --git a/tests/all/func.rs b/tests/all/func.rs
index e3bee038c..67e4f9ddc 100644
--- a/tests/all/func.rs
+++ b/tests/all/func.rs
@@ -774,3 +774,30 @@ fn wrap_multiple_results() -> anyhow::Result<()> {
         f64 "f64" F64 f64::from_bits(a),
     }
 }
+
+#[test]
+fn trampoline_for_declared_elem() -> anyhow::Result<()> {
+    let engine = Engine::default();
+
+    let module = Module::new(
+        &engine,
+        r#"
+            (module
+                (elem declare func $f)
+                (func $f)
+                (func (export "g") (result funcref)
+                  (ref.func $f)
+                )
+            )
+        "#,
+    )?;
+
+    let store = Store::new(&engine);
+    let instance = Instance::new(&store, &module, &[])?;
+
+    let g = instance.get_typed_func::<(), Option<Func>>("g")?;
+
+    let func = g.call(())?;
+    func.unwrap().call(&[])?;
+    Ok(())
+}