How to dlopen a side module larger than 4KB?

Viewed 705

I'm trying to dynamically load a side module from the main module using dlopen().

The side module loads fine as long as its size is smaller than 4KB but I need to load large-size side modules.

Here's a simple code you can test this:

side.c:

#define SIZE 4000
char dummy[SIZE] = {};

int side(int a)
{
    return SIZE;
}

main.c:

#include <stdio.h>
#include <dlfcn.h>

int main() 
{
    void *handle;
    typedef int (*func_t)(int);

    handle = dlopen("side.wasm", RTLD_NOW);

    if (!handle) {
        printf("failed to open the library\n");
        return 0;
    }
    func_t func = (func_t)dlsym(handle, "side");

    if (!func) {
        printf("failed to find the method\n");
        dlclose(handle);
        return 0;
    }
    printf("side module size: %d byte\n", func(1));
}

index.html:

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <script async src="main.js"></script>
</body>

</html>

commands:

emcc side.c -s SIDE_MODULE=1 -o side.wasm
emcc main.c -s MAIN_MODULE=1 -o main.html --preload-file side.wasm
python3 -m http.server 8080

And this is the result I get in the Chrome browser:

Error in loading dynamic library side.wasm: RangeError: WebAssembly.Compile is disallowed on the main thread, if the buffer size is larger than 4KB. Use WebAssembly.compile, or compile on a worker thread.

Can someone please guild me on how to dynamically load a side module larger than 4KB?

1 Answers

I found an alternative solution to do this which uses loadDynamicLibrary() and Asyncify instead of dlopen().

Here's the full working example code:

main.c:

#include <stdio.h>
#include <emscripten.h>

EM_JS(void, doLoadLibrary, (), {
    Asyncify.handleAsync(async() => {
    try {
        await loadDynamicLibrary('side.wasm', { loadAsync: true, global: true, nodelete: true, fs: FS });
        console.log('side module size: ' + Module['_side']());
    }
    catch (error) {
        console.log(error);
    }
});
    });

EMSCRIPTEN_KEEPALIVE
void loadLibrary() {
    printf("before\n");
    doLoadLibrary();
    printf("after\n");
}

int main()
{
}

side.c:

#define SIZE 5000
char dummy[SIZE] = {};

int side(void)
{
    return SIZE;
}

index.html:

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <input id="uploadLibrary" type="file" />
  <button id="loadLibrary">loadLibrary</button>
  <script>

    function uploadLibrary() {
      var files = this.files;
      if (files.length === 0) {
        console.log("No file is selected");
        return;
      }
      var file = files[0];
      var reader = new FileReader();
      reader.onload = function () {
        var data = new Uint8Array(reader.result);
        Module["FS_createDataFile"]("/", file.name, data, true, true, true);
      };
      reader.readAsArrayBuffer(file);
    }
    
    function loadLibrary() {
      Module._loadLibrary();
    }

    document.getElementById("uploadLibrary").addEventListener("change", uploadLibrary, false);
    document.getElementById("loadLibrary").addEventListener("click", loadLibrary, false);
  </script>
  <script async src="main.js"></script>
</body>

</html>

Makefile:

all: clean
    mkdir -p side
    emcc side.c -s SIDE_MODULE=1 -o side/side.wasm
    emcc main.c -s MAIN_MODULE=1 -s ASYNCIFY -s "ASYNCIFY_IMPORTS=['doLoadLibrary']" -s FORCE_FILESYSTEM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['FS']" -o main.html
    python3 -m http.server 8080

clean:
    rm -rf side main.html main.js main.wasm main.data

In your browser, click "Choose File" and select the side/side.wasm file, and then click "loadLibrary" button. You will see the following result in the console:

before
side module size: 5000
after
Related