Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document rust wasm32-unknown-emscripten build tool versions working with wasmer #2498

Closed
jcaesar opened this issue Jul 31, 2021 · 6 comments
Closed
Assignees
Labels
❓ question I've a question!

Comments

@jcaesar
Copy link
Contributor

jcaesar commented Jul 31, 2021

Summary

Finding a working combination of versions for rustc and emscripten is black magic, many fail with arcane error messages on missing C++ symbols. And the only working combination I found so far (emcc 1.39.20, rustc 1.47.0) runs me into #1898.

It would be really nice to document how to set up and environment that builds emscripten rust executables that can be run with wasmer.

Additional details

(non-working) env setup example:

FROM docker.io/library/debian:bullseye as bin
RUN apt-get update && \
    export DEBIAN_FRONTEND=noninteractive && \
    apt-get install -yq \
        build-essential \
        cmake \
        curl \
        file \
        git \
        python3 \
        python \
        sudo \
        && \
    apt-get clean && rm -rf /var/lib/apt/lists/* && \
    useradd rust --user-group --create-home --shell /bin/bash
USER rust
RUN cd /home/rust && git clone https://github.com/emscripten-core/emsdk.git && cd /home/rust/emsdk && ./emsdk install 1.39.20 && ./emsdk activate 1.39.20
ENV PATH=/home/rust/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.47 --profile default --no-modify-path && \
    rustup target add wasm32-unknown-emscripten
RUN cd ~ \
	&& git clone https://github.com/wasmerio/rust-cli-app-example ex \
	&& git config --global advice.detachedHead false \
	&& git -C ex checkout ee2f893d7d702f3e5efeee30633f7ef4f6723be5
WORKDIR /home/rust/ex
RUN bash -c ". /home/rust/emsdk/emsdk_env.sh && cargo -v build --release --locked --target=wasm32-unknown-emscripten"

FROM docker.io/library/busybox
RUN wget https://github.com/wasmerio/wasmer/releases/download/2.0.0/wasmer-linux-musl-amd64.tar.gz \
	&& echo e978d0e9298481bfd79b48a6446b63b8fcc6aa7560e22a37fb3a08705a88375b \ wasmer-linux-musl-amd64.tar.gz | sha256sum -c \
	&& tar xvf wasmer-linux-musl-amd64.tar.gz \
	&& rm wasmer-linux-musl-amd64.tar.gz
COPY --from=bin /home/rust/ex/target/wasm32-unknown-emscripten/release/rust_cli_app_example.wasm .
RUN wasmer run rust_cli_app_example.wasm -- -g
@jcaesar jcaesar added the ❓ question I've a question! label Jul 31, 2021
@jcaesar jcaesar changed the title Document how to build a working wasm32-unknown-emscripten executable Document how to build a working wasm32-unknown-emscripten executable from rust Aug 2, 2021
@Hywan
Copy link
Contributor

Hywan commented Aug 12, 2021

Hello,

I'm not sure it's the role of Wasmer to write documentation for emscripten. Maybe it must be part of the emscripten's official documentation. Or maybe just a code snippet in the README.md of the wasmer-emscripten crate.

@Hywan Hywan self-assigned this Aug 12, 2021
@Hywan Hywan added this to 📬 Backlog in Wasmer Runtime Issue Board via automation Aug 12, 2021
@Hywan Hywan moved this from 📬 Backlog to 🏁 Ready in Wasmer Runtime Issue Board Aug 12, 2021
@jcaesar
Copy link
Contributor Author

jcaesar commented Aug 12, 2021

Hi Hywan!

"Document how to build a wasm32-emscripten binary from rust" is definitely not wasmer's job, no. (Maybe Rust's, but I'm not sure either.)

But wasmer doesn't support binaries built by all versions of emscripten (at least #1898 makes it seem like that), and documenting which versions of emscripten are supported by wasmer is wasmer's job, imho.

Or maybe just a code snippet in the README.md of the wasmer-emscripten crate.

That'd be great.

@jcaesar jcaesar changed the title Document how to build a working wasm32-unknown-emscripten executable from rust Document how rust wasm32-unknown-emscripten build tool versions working with wasmer Aug 17, 2021
@jcaesar jcaesar changed the title Document how rust wasm32-unknown-emscripten build tool versions working with wasmer Document rust wasm32-unknown-emscripten build tool versions working with wasmer Aug 17, 2021
@jcaesar
Copy link
Contributor Author

jcaesar commented Oct 1, 2021

All right, I found a combination that looks like it would work, but then trying to run the binary just hangs. I'll have a deeper look at that another day.

Fockerdile
FROM docker.io/library/debian:bullseye as bin
RUN apt-get update && \
    export DEBIAN_FRONTEND=noninteractive && \
    apt-get install -yq \
        build-essential \
        cmake \
        curl \
        file \
        git \
        python3 \
        python \
        sudo \
        && \
    apt-get clean && rm -rf /var/lib/apt/lists/* && \
    useradd rust --user-group --create-home --shell /bin/bash
USER rust
RUN cd /home/rust && git clone https://github.com/emscripten-core/emsdk.git && cd /home/rust/emsdk && ./em
sdk install 1.38.43 && ./emsdk activate 1.38.43
ENV PATH=/home/rust/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain 1.34 --profile default --no-modify-pa
th && \
    rustup target add wasm32-unknown-emscripten
RUN cd ~ \
        && git clone https://github.com/wasmerio/rust-cli-app-example ex \
        && git config --global advice.detachedHead false \
        && git -C ex checkout ee2f893d7d702f3e5efeee30633f7ef4f6723be5
WORKDIR /home/rust/ex
RUN mkdir -p .cargo \
        && echo >>.cargo/config \
        && echo >>.cargo/config [target.wasm32-unknown-emscripten] \
        && echo >>.cargo/config 'rustflags = [ "-C", "link-args=-s EMITTING_JS=1" ]' \
        && echo >>.cargo/config
# EMITTING_JS is the old version of STANDALONE_WASM.
# It's necessary because emscripten would minify import/export names on release builds.
# Alternatively, change -O3 to -O2
#RUN true \
#       && echo >>Cargo.toml \
#       && echo >>Cargo.toml [profile.release] \
#       && echo >>Cargo.toml opt-level = '2' \
#       && echo >>Cargo.toml
#RUN cat .cargo/config && false
RUN bash -c ". /home/rust/emsdk/emsdk_env.sh && cargo -v build --locked --target=wasm32-unknown-emscripten
"

FROM docker.io/library/busybox
RUN wget https://github.com/wasmerio/wasmer/releases/download/2.0.0/wasmer-linux-musl-amd64.tar.gz \
        && echo e978d0e9298481bfd79b48a6446b63b8fcc6aa7560e22a37fb3a08705a88375b \ wasmer-linux-musl-amd64
.tar.gz | sha256sum -c \
        && tar xvf wasmer-linux-musl-amd64.tar.gz \
        && rm wasmer-linux-musl-amd64.tar.gz
COPY --from=bin /home/rust/ex/target/wasm32-unknown-emscripten/debug/rust_cli_app_example.wasm .
#ENTRYPOINT ["/bin/sh", "inspect", "rust_cli_app_example.wasm"]
ENTRYPOINT ["/bin/wasmer", "run", "rust_cli_app_example.wasm", "--disable-cache", "--"]

@syrusakbary
Copy link
Member

Awesome, closing the issue.

Wasmer Runtime Issue Board automation moved this from 🏁 Ready to 🎉 Done Oct 20, 2021
@jcaesar
Copy link
Contributor Author

jcaesar commented Oct 20, 2021

Not really working yet. I'll reopen if I have further ideas..

@jcaesar
Copy link
Contributor Author

jcaesar commented Jan 26, 2022

All right, I dug a bit deeper.
My Dockerfile gets me the following executable: rust_cli_app_example.wasm.gz

If I run this in wasmer

#0  0x00007ffff7f5d600 in __lll_lock_wait () from /usr/lib/libpthread.so.0
#1  0x00007ffff7f56503 in pthread_mutex_lock () from /usr/lib/libpthread.so.0
#2  0x000055555602263e in std::sys::unix::mutex::Mutex::lock (self=0x5555587647b0) at /rustc/1.58.0/library/std/src/sys/unix/mutex.rs:63
#3  0x0000555555fbc97d in std::sys_common::mutex::MovableMutex::raw_lock (self=0x555558975830) at /rustc/1.58.0/library/std/src/sys_common/mutex.rs:76
#4  0x0000555555e61fe5 in std::sync::mutex::Mutex<wasmer_emscripten::EmscriptenData>::lock<wasmer_emscripten::EmscriptenData> (self=0x555558975830) at /rustc/1.58.0/library/std/src/sync/mutex.rs:267
#5  0x0000555555e81238 in wasmer_emscripten::env::get_emscripten_data (ctx=0x555558866be0) at lib/emscripten/src/env/mod.rs:57
#6  0x0000555555ffe0d6 in wasmer_emscripten::emscripten_target::invoke_i (ctx=0x555558866be0, index=3215) at lib/emscripten/src/emscripten_target.rs:175
#7  0x0000555555e42920 in core::ops::function::Fn::call<fn(&wasmer_emscripten::EmEnv, i32) -> i32, (&wasmer_emscripten::EmEnv, i32)> () at /rustc/1.58.0/library/core/src/ops/function.rs:70
#8  0x0000555555ec70fd in wasmer::sys::externals::function::inner::{impl#31}::function_body_ptr::func_wrapper::{closure#0}<i32, i32, i32, wasmer_emscripten::EmEnv, fn(&wasmer_emscripten::EmEnv, i32) -> i32> () at /home/julius/code/wasmer/lib/api/src/sys/externals/function.rs:1357
#9  0x0000555556016b19 in core::panic::unwind_safe::{impl#23}::call_once<core::result::Result<i32, core::convert::Infallible>, wasmer::sys::externals::function::inner::{impl#31}::function_body_ptr::func_wrapper::{closure#0}> (self=..., _args=()) at /rustc/1.58.0/library/core/src/panic/unwind_safe.rs:271
#10 0x0000555555fe488f in std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<wasmer::sys::externals::function::inner::{impl#31}::function_body_ptr::func_wrapper::{closure#0}>, core::result::Result<i32, core::convert::Infallible>> (data=0x7fffffff83f0) at /rustc/1.58.0/library/std/src/panicking.rs:406
#11 0x0000555555ffa0fb in __rust_try ()
#12 0x0000555555fd2344 in std::panicking::try<core::result::Result<i32, core::convert::Infallible>, core::panic::unwind_safe::AssertUnwindSafe<wasmer::sys::externals::function::inner::{impl#31}::function_body_ptr::func_wrapper::{closure#0}>> (f=...) at /rustc/1.58.0/library/std/src/panicking.rs:370
#13 0x0000555555f96fb1 in std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<wasmer::sys::externals::function::inner::{impl#31}::function_body_ptr::func_wrapper::{closure#0}>, core::result::Result<i32, core::convert::Infallible>> (f=...) at /rustc/1.58.0/library/std/src/panic.rs:133
#14 0x0000555555ec0f52 in wasmer::sys::externals::function::inner::{impl#31}::function_body_ptr::func_wrapper<i32, i32, i32, wasmer_emscripten::EmEnv, fn(&wasmer_emscripten::EmEnv, i32) -> i32> (env=0x555558866be0, A1=3215) at /home/julius/code/wasmer/lib/api/src/sys/externals/function.rs:1356
#15 0x00007ffff5c366e1 in ?? ()
#16 0x0000000000000000 in ?? ()

It looks like wasmer is running in a deadlock somewhere in its emscripten code. :(

@syrusakbary Would you mind reopening this? The issue is very much still there, after all. (I should probably open a separate issue for the deadlock.)

[Edit:] I tried turning no_deadlocks loose on it, without much luck

[1643205484.824 DEBUG wasmer_emscripten::emscripten_target] emscripten::invoke_i
=========== REPORT START ===========
A reentrance has been attempted, but `std::sync`'s locks are not reentrant. This results in a deadlock. dependence cycle: [Thread(ThreadId(1)), Lock(0)]
Lock taken at:
   0: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /usr/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:271:9
   1: std::panicking::try::do_call
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
   2: __rust_try
   3: std::panicking::try
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
   4: std::panic::catch_unwind
             at /usr/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
   5: <Func as wasmer::sys::externals::function::inner::HostFunction<(A1,A2),Rets,wasmer::sys::externals::function::inner::WithEnv,Env>>::function_body_ptr::func_wrapper
             at /home/julius/code/wasmer/lib/api/src/sys/externals/function.rs:1356:38
   6: <unknown>

Reentrace at:
   0: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /usr/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:271:9
   1: std::panicking::try::do_call
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
   2: __rust_try
   3: std::panicking::try
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
   4: std::panic::catch_unwind
             at /usr/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
   5: <Func as wasmer::sys::externals::function::inner::HostFunction<A1,Rets,wasmer::sys::externals::function::inner::WithEnv,Env>>::function_body_ptr::func_wrapper
             at /home/julius/code/wasmer/lib/api/src/sys/externals/function.rs:1356:38
   6: <unknown>

=========== REPORT END ===========

thread 'main' panicked at 'DEADLOCK DETECTED! See stderr for details', /home/julius/.cargo/registry/src/github.com-1ecc6299db9ec823/no_deadlocks-1.3.0/src/lock_manager.rs:267:9
stack backtrace:
   0: rust_begin_unwind
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:498:5
   1: core::panicking::panic_fmt
             at /usr/lib/rustlib/src/rust/library/core/src/panicking.rs:107:14
   2: no_deadlocks::lock_manager::LockManagerInner::handle_deadlock
             at /home/julius/.cargo/registry/src/github.com-1ecc6299db9ec823/no_deadlocks-1.3.0/src/lock_manager.rs:267:9
   3: no_deadlocks::lock_manager::LockManagerInner::analyse
             at /home/julius/.cargo/registry/src/github.com-1ecc6299db9ec823/no_deadlocks-1.3.0/src/lock_manager.rs:188:13
   4: no_deadlocks::mutex::Mutex<T>::lock
             at /home/julius/.cargo/registry/src/github.com-1ecc6299db9ec823/no_deadlocks-1.3.0/src/mutex.rs:100:17
   5: wasmer_emscripten::env::get_emscripten_data
             at /home/julius/code/wasmer/lib/emscripten/src/env/mod.rs:56:5
   6: wasmer_emscripten::emscripten_target::invoke_i
             at /home/julius/code/wasmer/lib/emscripten/src/emscripten_target.rs:176:18
   7: core::ops::function::Fn::call
             at /usr/lib/rustlib/src/rust/library/core/src/ops/function.rs:70:5
   8: <Func as wasmer::sys::externals::function::inner::HostFunction<A1,Rets,wasmer::sys::externals::function::inner::WithEnv,Env>>::function_body_ptr::func_wrapper::{{closure}}
             at /home/julius/code/wasmer/lib/api/src/sys/externals/function.rs:1357:29
   9: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /usr/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:271:9
  10: std::panicking::try::do_call
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:406:40
  11: __rust_try
  12: std::panicking::try
             at /usr/lib/rustlib/src/rust/library/std/src/panicking.rs:370:19
  13: std::panic::catch_unwind
             at /usr/lib/rustlib/src/rust/library/std/src/panic.rs:133:14
  14: <Func as wasmer::sys::externals::function::inner::HostFunction<A1,Rets,wasmer::sys::externals::function::inner::WithEnv,Env>>::function_body_ptr::func_wrapper
             at /home/julius/code/wasmer/lib/api/src/sys/externals/function.rs:1356:38
  15: <unknown>
[Edit2:] All right, I have a fix for it, I will make a proper PR tomorrow.
diff --git a/lib/emscripten/src/emscripten_target.rs b/lib/emscripten/src/emscripten_target.rs
index 790e1dd3e..f5e398c41 100644
--- a/lib/emscripten/src/emscripten_target.rs
+++ b/lib/emscripten/src/emscripten_target.rs
@@ -140,7 +140,8 @@ pub fn _getnameinfo(
macro_rules! invoke {
   ($ctx: ident, $name:ident, $name_ref:ident, $( $arg:ident ),*) => {{
       let sp = get_emscripten_data($ctx).stack_save_ref().expect("stack_save is None").call().expect("stack_save call failed");
-        let result = get_emscripten_data($ctx).$name_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*);
+        let f = get_emscripten_data($ctx).$name_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).clone();
+        let result = f.call($($arg),*);
       match result {
           Ok(v) => v,
           Err(_e) => {
@@ -156,7 +157,8 @@ macro_rules! invoke {
macro_rules! invoke_no_return {
   ($ctx: ident, $name:ident, $name_ref:ident, $( $arg:ident ),*) => {{
       let sp = get_emscripten_data($ctx).stack_save_ref().expect("stack_save is None").call().expect("stack_save call failed");
-        let result = get_emscripten_data($ctx).$name_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).call($($arg),*);
+        let f = get_emscripten_data($ctx).$name_ref().expect(concat!("Dynamic call is None: ", stringify!($name))).clone();
+        let result = f.call($($arg),*);
       match result {
           Ok(v) => v,
           Err(_e) => {
@@ -172,7 +174,33 @@ macro_rules! invoke_no_return {
// Invoke functions
pub fn invoke_i(ctx: &EmEnv, index: i32) -> i32 {
   debug!("emscripten::invoke_i");
-    invoke!(ctx, dyn_call_i, dyn_call_i_ref, index)
+    {
+        let sp = get_emscripten_data(ctx)
+            .stack_save_ref()
+            .expect("stack_save is None")
+            .call()
+            .expect("stack_save call failed");
+        let result = get_emscripten_data(ctx)
+            .dyn_call_i_ref()
+            .expect("Dynamic call is None: dyn_call_i")
+            .call(index);
+        match result {
+            Ok(v) => v,
+            Err(_e) => {
+                get_emscripten_data(ctx)
+                    .stack_restore_ref()
+                    .expect("stack_restore is None")
+                    .call(sp)
+                    .expect("stack_restore call failed");
+                get_emscripten_data(ctx)
+                    .set_threw_ref()
+                    .expect("set_threw is None")
+                    .call(1, 0)
+                    .expect("set_threw call failed");
+                0 as _
+            }
+        }
+    }
}
pub fn invoke_ii(ctx: &EmEnv, index: i32, a1: i32) -> i32 {
   debug!("emscripten::invoke_ii");
@@ -196,7 +224,35 @@ pub fn invoke_v(ctx: &EmEnv, index: i32) {
}
pub fn invoke_vi(ctx: &EmEnv, index: i32, a1: i32) {
   debug!("emscripten::invoke_vi");
-    invoke_no_return!(ctx, dyn_call_vi, dyn_call_vi_ref, index, a1);
+    {
+        let sp = get_emscripten_data(ctx)
+            .stack_save_ref()
+            .expect("stack_save is None")
+            .call()
+            .expect("stack_save call failed");
+        let f = get_emscripten_data(ctx)
+            .dyn_call_vi_ref()
+            .expect(concat!("Dynamic call is None: ", stringify!(dyn_call_vi)))
+            .clone();
+        let result = f.call(index, a1);
+        match result {
+            Ok(v) => v,
+            Err(_e) => {
+                get_emscripten_data(ctx)
+                    .stack_restore_ref()
+                    .expect("stack_restore is None")
+                    .call(sp)
+                    .expect("stack_restore call failed");
+                // TODO: We should check if _e != "longjmp" and if that's the case, re-throw the error
+                // JS version is: if (e !== e+0 && e !== 'longjmp') throw e;
+                get_emscripten_data(ctx)
+                    .set_threw_ref()
+                    .expect("set_threw is None")
+                    .call(1, 0)
+                    .expect("set_threw call failed");
+            }
+        }
+    }
}
pub fn invoke_vii(ctx: &EmEnv, index: i32, a1: i32, a2: i32) {
   debug!("emscripten::invoke_vii");
@@ -604,52 +660,52 @@ pub fn invoke_iiijj(
}
pub fn invoke_j(ctx: &EmEnv, index: i32) -> i32 {
   debug!("emscripten::invoke_j");
-    if let Some(dyn_call_j) = get_emscripten_data(ctx).dyn_call_j_ref() {
-        dyn_call_j.call(index).unwrap()
-    } else {
-        panic!("dyn_call_j is set to None");
-    }
+    let dyn_call_j = get_emscripten_data(ctx)
+        .dyn_call_j_ref()
+        .expect("dyn_call_j is set to None")
+        .clone();
+    dyn_call_j.call(index).unwrap()
}
pub fn invoke_ji(ctx: &EmEnv, index: i32, a1: i32) -> i32 {
   debug!("emscripten::invoke_ji");
-    if let Some(dyn_call_ji) = get_emscripten_data(ctx).dyn_call_ji_ref() {
-        dyn_call_ji.call(index, a1).unwrap()
-    } else {
-        panic!("dyn_call_ji is set to None");
-    }
+    let dyn_call_ji = get_emscripten_data(ctx)
+        .dyn_call_ji_ref()
+        .expect("dyn_call_ji is set to None")
+        .clone();
+    dyn_call_ji.call(index, a1).unwrap()
}
pub fn invoke_jii(ctx: &EmEnv, index: i32, a1: i32, a2: i32) -> i32 {
   debug!("emscripten::invoke_jii");
-    if let Some(dyn_call_jii) = get_emscripten_data(ctx).dyn_call_jii_ref() {
-        dyn_call_jii.call(index, a1, a2).unwrap()
-    } else {
-        panic!("dyn_call_jii is set to None");
-    }
+    let dyn_call_jii = get_emscripten_data(ctx)
+        .dyn_call_jii_ref()
+        .expect("dyn_call_jii is set to None")
+        .clone();
+    dyn_call_jii.call(index, a1, a2).unwrap()
}

pub fn invoke_jij(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32) -> i32 {
   debug!("emscripten::invoke_jij");
-    if let Some(dyn_call_jij) = get_emscripten_data(ctx).dyn_call_jij_ref() {
-        dyn_call_jij.call(index, a1, a2, a3).unwrap()
-    } else {
-        panic!("dyn_call_jij is set to None");
-    }
+    let dyn_call_jij = get_emscripten_data(ctx)
+        .dyn_call_jij_ref()
+        .expect("dyn_call_jij is set to None")
+        .clone();
+    dyn_call_jij.call(index, a1, a2, a3).unwrap()
}
pub fn invoke_jjj(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) -> i32 {
   debug!("emscripten::invoke_jjj");
-    if let Some(dyn_call_jjj) = get_emscripten_data(ctx).dyn_call_jjj_ref() {
-        dyn_call_jjj.call(index, a1, a2, a3, a4).unwrap()
-    } else {
-        panic!("dyn_call_jjj is set to None");
-    }
+    let dyn_call_jjj = get_emscripten_data(ctx)
+        .dyn_call_jjj_ref()
+        .expect("dyn_call_jjj is set to None")
+        .clone();
+    dyn_call_jjj.call(index, a1, a2, a3, a4).unwrap()
}
pub fn invoke_viiij(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
   debug!("emscripten::invoke_viiij");
-    if let Some(dyn_call_viiij) = get_emscripten_data(ctx).dyn_call_viiij_ref() {
-        dyn_call_viiij.call(index, a1, a2, a3, a4, a5).unwrap();
-    } else {
-        panic!("dyn_call_viiij is set to None");
-    }
+    let dyn_call_viiij = get_emscripten_data(ctx)
+        .dyn_call_viiij_ref()
+        .expect("dyn_call_viiij is set to None")
+        .clone();
+    dyn_call_viiij.call(index, a1, a2, a3, a4, a5).unwrap();
}
pub fn invoke_viiijiiii(
   ctx: &EmEnv,
@@ -665,13 +721,13 @@ pub fn invoke_viiijiiii(
   a9: i32,
) {
   debug!("emscripten::invoke_viiijiiii");
-    if let Some(dyn_call_viiijiiii) = get_emscripten_data(ctx).dyn_call_viiijiiii_ref() {
-        dyn_call_viiijiiii
-            .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9)
-            .unwrap();
-    } else {
-        panic!("dyn_call_viiijiiii is set to None");
-    }
+    let dyn_call_viiijiiii = get_emscripten_data(ctx)
+        .dyn_call_viiijiiii_ref()
+        .expect("dyn_call_viiijiiii is set to None")
+        .clone();
+    dyn_call_viiijiiii
+        .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9)
+        .unwrap();
}
pub fn invoke_viiijiiiiii(
   ctx: &EmEnv,
@@ -689,29 +745,29 @@ pub fn invoke_viiijiiiiii(
   a11: i32,
) {
   debug!("emscripten::invoke_viiijiiiiii");
-    if let Some(dyn_call_viiijiiiiii) = get_emscripten_data(ctx).dyn_call_viiijiiiiii_ref() {
-        dyn_call_viiijiiiiii
-            .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)
-            .unwrap();
-    } else {
-        panic!("dyn_call_viiijiiiiii is set to None");
-    }
+    let dyn_call_viiijiiiiii = get_emscripten_data(ctx)
+        .dyn_call_viiijiiiiii_ref()
+        .expect("dyn_call_viiijiiiiii is set to None")
+        .clone();
+    dyn_call_viiijiiiiii
+        .call(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11)
+        .unwrap();
}
pub fn invoke_viij(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) {
   debug!("emscripten::invoke_viij");
-    if let Some(dyn_call_viij) = get_emscripten_data(ctx).dyn_call_viij_ref() {
-        dyn_call_viij.call(index, a1, a2, a3, a4).unwrap();
-    } else {
-        panic!("dyn_call_viij is set to None");
-    }
+    let dyn_call_viij = get_emscripten_data(ctx)
+        .dyn_call_viij_ref()
+        .expect("dyn_call_viij is set to None")
+        .clone();
+    dyn_call_viij.call(index, a1, a2, a3, a4).unwrap();
}
pub fn invoke_viiji(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
   debug!("emscripten::invoke_viiji");
-    if let Some(dyn_call_viiji) = get_emscripten_data(ctx).dyn_call_viiji_ref() {
-        dyn_call_viiji.call(index, a1, a2, a3, a4, a5).unwrap();
-    } else {
-        panic!("dyn_call_viiji is set to None");
-    }
+    let dyn_call_viiji = get_emscripten_data(ctx)
+        .dyn_call_viiji_ref()
+        .expect("dyn_call_viiji is set to None")
+        .clone();
+    dyn_call_viiji.call(index, a1, a2, a3, a4, a5).unwrap();
}
pub fn invoke_viijiii(
   ctx: &EmEnv,
@@ -725,29 +781,29 @@ pub fn invoke_viijiii(
   a7: i32,
) {
   debug!("emscripten::invoke_viijiii");
-    if let Some(dyn_call_viijiii) = get_emscripten_data(ctx).dyn_call_viijiii_ref() {
-        dyn_call_viijiii
-            .call(index, a1, a2, a3, a4, a5, a6, a7)
-            .unwrap();
-    } else {
-        panic!("dyn_call_viijiii is set to None");
-    }
+    let dyn_call_viijiii = get_emscripten_data(ctx)
+        .dyn_call_viijiii_ref()
+        .expect("dyn_call_viijiii is set to None")
+        .clone();
+    dyn_call_viijiii
+        .call(index, a1, a2, a3, a4, a5, a6, a7)
+        .unwrap();
}
pub fn invoke_viijj(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32, a6: i32) {
   debug!("emscripten::invoke_viijj");
-    if let Some(dyn_call_viijj) = get_emscripten_data(ctx).dyn_call_viijj_ref() {
-        dyn_call_viijj.call(index, a1, a2, a3, a4, a5, a6).unwrap();
-    } else {
-        panic!("dyn_call_viijj is set to None");
-    }
+    let dyn_call_viijj = get_emscripten_data(ctx)
+        .dyn_call_viijj_ref()
+        .expect("dyn_call_viijj is set to None")
+        .clone();
+    dyn_call_viijj.call(index, a1, a2, a3, a4, a5, a6).unwrap();
}
pub fn invoke_vj(ctx: &EmEnv, index: i32, a1: i32, a2: i32) {
   debug!("emscripten::invoke_vj");
-    if let Some(dyn_call_vj) = get_emscripten_data(ctx).dyn_call_vj_ref() {
-        dyn_call_vj.call(index, a1, a2).unwrap();
-    } else {
-        panic!("dyn_call_vj is set to None");
-    }
+    let dyn_call_vj = get_emscripten_data(ctx)
+        .dyn_call_vj_ref()
+        .expect("dyn_call_vj is set to None")
+        .clone();
+    dyn_call_vj.call(index, a1, a2).unwrap();
}
pub fn invoke_vjji(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
   debug!("emscripten::invoke_vjji");
@@ -765,19 +821,19 @@ pub fn invoke_vjji(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32,
}
pub fn invoke_vij(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32) {
   debug!("emscripten::invoke_vij");
-    if let Some(dyn_call_vij) = get_emscripten_data(ctx).dyn_call_vij_ref() {
-        dyn_call_vij.call(index, a1, a2, a3).unwrap();
-    } else {
-        panic!("dyn_call_vij is set to None");
-    }
+    let dyn_call_vij = get_emscripten_data(ctx)
+        .dyn_call_vij_ref()
+        .expect("dyn_call_vij is set to None")
+        .clone();
+    dyn_call_vij.call(index, a1, a2, a3).unwrap();
}
pub fn invoke_viji(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32) {
   debug!("emscripten::invoke_viji");
-    if let Some(dyn_call_viji) = get_emscripten_data(ctx).dyn_call_viji_ref() {
-        dyn_call_viji.call(index, a1, a2, a3, a4).unwrap()
-    } else {
-        panic!("dyn_call_viji is set to None");
-    }
+    let dyn_call_viji = get_emscripten_data(ctx)
+        .dyn_call_viji_ref()
+        .expect("dyn_call_viji is set to None")
+        .clone();
+    dyn_call_viji.call(index, a1, a2, a3, a4).unwrap()
}
pub fn invoke_vijiii(
   ctx: &EmEnv,
@@ -790,19 +846,19 @@ pub fn invoke_vijiii(
   a6: i32,
) {
   debug!("emscripten::invoke_vijiii");
-    if let Some(dyn_call_vijiii) = get_emscripten_data(ctx).dyn_call_vijiii_ref() {
-        dyn_call_vijiii.call(index, a1, a2, a3, a4, a5, a6).unwrap()
-    } else {
-        panic!("dyn_call_vijiii is set to None");
-    }
+    let dyn_call_vijiii = get_emscripten_data(ctx)
+        .dyn_call_vijiii_ref()
+        .expect("dyn_call_vijiii is set to None")
+        .clone();
+    dyn_call_vijiii.call(index, a1, a2, a3, a4, a5, a6).unwrap()
}
pub fn invoke_vijj(ctx: &EmEnv, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
   debug!("emscripten::invoke_vijj");
-    if let Some(dyn_call_vijj) = get_emscripten_data(ctx).dyn_call_vijj_ref() {
-        dyn_call_vijj.call(index, a1, a2, a3, a4, a5).unwrap()
-    } else {
-        panic!("dyn_call_vijj is set to None");
-    }
+    let dyn_call_vijj = get_emscripten_data(ctx)
+        .dyn_call_vijj_ref()
+        .expect("dyn_call_vijj is set to None")
+        .clone();
+    dyn_call_vijj.call(index, a1, a2, a3, a4, a5).unwrap()
}
pub fn invoke_vidd(ctx: &EmEnv, index: i32, a1: i32, a2: f64, a3: f64) {
   debug!("emscripten::invoke_viid");

bors bot added a commit that referenced this issue Jan 27, 2022
2769: Deadlock in emscripten dynamic calls r=Amanieu a=jcaesar

# Description

Attempting to run [an emscripten-built](#2498 (comment)) rust module lead me to a deadlock. I'm not quite sure what's wrong, but it looks to me like emscripten is calling itself through the host (No idea why. To catch exceptions?) and in the process, wasmer acquires a lock that is still held when calling back into the module. If the module tries that twice recursively, the simplest form of deadlock, a reentrancy deadlock is triggered.

The standard library doesn't have reentrant locks.
This cannot be solved by replacing the lock on `EmEnv.data` by an RwLock, because the code might also call `setTempRet0`, which does require a write lock. 
Maybe there is a way to get rid of the mutex entirely, but I do not see it.

The easy way out seems to be to clone the function (arcs), let go of the lock, and then do the call back into the module.

There are a few other instances of the same problematic pattern, but they call `memset` or `malloc`, which shouldn't call back into wasmer. I assume they are fine and left them alone, but have not done thorough testing.

# Review

- [x] Add a short description of the change to the CHANGELOG.md file



Co-authored-by: Julius Michaelis <glitter@liftm.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
❓ question I've a question!
Projects
Development

No branches or pull requests

3 participants