emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.6k stars 3.28k forks source link

No DynCalls in STANDALONE_WASM? #15894

Open rygo6 opened 2 years ago

rygo6 commented 2 years ago

I am making a build with --no-entry -s STANDALONE_WASM set and have noticed there are no DynCall exports in it. After perusing the change log, I inferred the first param on an invoke_x import is supposed to be the index offset from the __indirect_function_table, is that correct?

I have gone through and tried to implement it so the first param passed to an invoke_x method is used as the index on the table.get method but I seem to get errors sometimes that say undefined element: out of bounds table access. Am I doing this correctly, or is there something more to implementing these dyncalls?

sbc100 commented 2 years ago

The code we use to implementing the invoke_x wrappers is here:

https://github.com/emscripten-core/emscripten/blob/8ee5d7db04d73e072c39b2316f355d73e9967af8/tools/js_manipulation.py#L114

It general it should be possible to implement them using wasmTable.get(). See:

https://github.com/emscripten-core/emscripten/blob/8ee5d7db04d73e072c39b2316f355d73e9967af8/tools/js_manipulation.py#L111

It the index way out of bounds? Can you log the index to see what it is? How does it compare to the side of the table?

sbc100 commented 2 years ago

I assume that you need to implement these invoke functions because you are wanting to use exception handling? Is that right? Or is it for longjmp?

rygo6 commented 2 years ago

I believe it is for both? I am trying to get a fairly intricate wasm build to work on top of wasmtime, which doesn't have an up-to-date emscripten ABI, so I was just filling in the missing methods. Is there some better way to approach this I may not know about?

Unfortunately, the out of bounds error isn't saying anything about what the bounds is.

sbc100 commented 2 years ago

Implementing the invoke_x functions requires the host environment to be able to do try/catch and continue. I don't know how easy that is to do in wasmtime.

Regarding debugging, given that you are implementing invoke_x in wasmtime, can't you just log that index that you are being passed and then log the size of the table (I assume you are implementing this stuff inside of wasmtime itself somehow?)

kripken commented 2 years ago

If you want to run code with invokes in a place like wasmtime then Asyncify is an option for now, until wasmtime gets wasm exceptions support. For example there is a CRuby port doing that. But this does require some low-level work (see the "pure wasm" section in the first link).

Or, you could perhaps add custom invoke_x implementations to a wasm VM. I believe the wasmer runtime has that already, but you could add it in a fork of wasmtime too.

rygo6 commented 2 years ago

So I am actually just proof-of-concepting this out right now in .NET with wastime dotnot plugin. This is what I ended up writing:

            m_Linker.Define("env", "invoke_iii", Function.FromCallback(m_Store, (int index, int a1, int a2) =>
                {
                    Debug.Log($"invoke_iii {index} {a1} {a2}");
                    var s = m_StackSaveFunc.Invoke(m_Store);
                    try
                    {
                        if (m_Table.GetElement(m_Store, (uint) index) is Function func)
                            return func.Invoke(m_Store, a1, a2) as int? ?? 0;
                        Debug.LogError($"invoke_iii func not found!");
                    }
                    catch (Exception e)
                    {
                        Debug.LogError($"invoke_iii {index} {a1} {a2} {e}");
                        if (s is int stack)
                            m_StackRestoreFunc.Invoke(m_Store, stack);
                    }
                    return 0;
                })
            );

But now that I look at it, it appears that my undefined element: out of bounds table access may not actually be from the specific invoke, all the indices are in range. But something internally that the invoked method calls. Seem weird I could compile wasm that could somehow try to access something outside the table?

sbc100 commented 2 years ago

undefined element: out of bounds table access can be the result of function pointer corruption which in turn can just be memory corruption, so there are lots of things could cause that.