Is it possible to add a new function to an instantiated Webassembly module?

865 views Asked by At

I'm writing a wasm program that will dynamically generate wasm functions as bytecode (including the type signature, locals vector, and body instruction sequence; everything that defines a function according to the spec). I want an efficient way to execute these functions (i.e. be able to obtain a funcref to them) from within an already-instantiated, running module.

It seems like most implementations do this sort of thing by simply creating a new module out of the generated code, hooking up the necessary imports, and then invoking the new module from JavaScript.

I need to do it without JavaScript, and ideally without creating a new module either. It seems like this should be doable in a relatively simple way:

  1. Just add the new function to the current module's existing vector of functions, with a new funcidx. Obviously, care must be taken to ensure the generated code references other functions, globals, imports etc. by their appropriate indices.
  2. Reference the new function by it's new funcidx, including calling ref.func to get a funcref to call it indirectly.

Based on my understanding of wasm, step 1 is impossible because there's no instruction to add a new function to the default funcref table. Might this be subject to change in the future? It's a bit difficult to navigate all the wasm spec proposals so was hoping this post might get attention from someone who works on this problem to at least link to some hints on the current state of affairs.

If an actual instruction in the spec is a non-starter, it seems this may be doable alternatively via a runtime API such as WASI, which could introduce an API method to modify the currently-running module in-place. AFAICT WASI does not currently design for this nor does it have plans to. Am I wrong about that or is there another runtime interface that does plan to do this?

2

There are 2 answers

0
sbc100 On

WebAssembly does not support adding or removing functions (or any other elements) from running instances, and I don't know of any proposals to add such as feature.

To create a new function (e.g. when writing a JIT) you currently need to create a new module. Is there a particular reason you want to avoid this route?

0
William Stein On

WebAssembly does support adding functions to running instances via the function table, which can be imported from the host environment.
A very nontrivial example of this is that Emscripten implements dynamic linking of entirely separate WebAssembly modules with existing ones (https://emscripten.org/docs/compiling/Dynamic-Linking.html). In particular, Emscripten solves the problem of adding functions (with very high performance) to a running WebAssembly instance. The implementation of dlopen in Emscripten uses that the function table can be modified externally.

If you go to https://pyodide.org/en/stable/console.html and type "import numpy", then "numpy.array(10)", then (1) all the C code of numpy gets added to the running Python Wasm instance after it is instantiated, and (2) you just called one of those C functions from the Python WASM instance (the REPL is part of it). This is also very fast, e.g., it's not going through a slow Javascript layer.

This builds on top of the existing WebAssembly spec. It's nontrivial though! E.g., a key component of making this work is the wasm32-unknown-emscripten target that's part of LLVM, which generates actual position independent code (-fPIC). I don't know if there is any other WebAssembly language besides C/C++ with support for -fPIC. See https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md Also, if you look at emscripten itself, e.g., https://github.com/emscripten-core/emscripten/blob/main/src/library_dylink.js there's code in there to parse the wasm dynamic library, etc.