如何将外部 C 库链接到 WebAssembly 构建

Tim*_*ler 5 zlib webassembly

我正在阅读这篇文章(https://www.smashingmagazine.com/2019/04/webassembly-speed-web-app/),其中解释了他们如何使用 zlib 等来加速他们的 Web 项目:

为了支持 zlib 库,我们使用标志 USE_ZLIB;zlib 是如此普遍以至于它已经被移植到 WebAssembly,并且 Emscripten 会在我们的项目中为我们包含它

我想在我自己的 WASM 模块中使用 zlib。

在我的 C 代码(用 emcc 编译)中,我编写了这个接口函数:

#include <zlib.h>

int pcf_decompress_zlib(unsigned char *input, int input_length, unsigned char *output, int output_length)
{
    uLongf output_length_result = output_length;
    int result = uncompress(output, &output_length_result, input, input_length);
    if (result != Z_OK) {
        return 0;
    } else {
        return output_length_result;
    }
}
Run Code Online (Sandbox Code Playgroud)

我是这样编译的:

emcc decompress.c -O3 -s WASM=1 -s SIDE_MODULE=1 -s "EXPORTED_FUNCTIONS=['_pcf_decompress_zlib']" -s USE_ZLIB=1 -o decompress.wasm
Run Code Online (Sandbox Code Playgroud)

当我这样做时,emcc 自动下载到一个 zlib 库中,所以它似乎知道如何处理这个。

然后在浏览器中,我有这个类:

export class Decompressor {
    wasmOnLoad(obj) {
        this.instance = obj.instance;
        console.log("Loaded WASM");
        console.log(obj.instance);
        // Don't do anything else yet
    }

    constructor() {
        this.memory = new WebAssembly.Memory({
            initial: 1
        });
        this.heap = new Uint8Array(this.memory.buffer);
        this.imports = {
            env: {
                __memory_base: 0,
                memory: this.memory,
                abort: function(err) {
                    throw new Error('abort ' + err);
                },
            }
        };
    }

    start() {
        console.log("startWasm");
        WebAssembly.instantiateStreaming(fetch('decompress/decompress.wasm'), this.imports)
            .then(this.wasmOnLoad.bind(this));
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在从我的 HTML 加载的主要 JS 代码中:

import { Decompressor } from "./decompress/decompress.js";
var l = new Decompressor();
l.start();
Run Code Online (Sandbox Code Playgroud)

当我加载页面时,Firefox 给了我这个错误:

LinkError: import object field '_uncompress' is not a Function
Run Code Online (Sandbox Code Playgroud)

发出的 wasm 代码似乎不包含 zlib,而且 zlib 也未内置到浏览器中。我想SIDE_MODULE改为MAIN_MODULE,但这导致了数十个未定义的符号,使问题变得更糟。

USE_ZLIB=1如果emcc没有自动使 zlib 可用,那么让 emcc 提供一个选项是没有意义的。那么我缺少什么才能使这项工作发挥作用?我如何让 emcc 将它已经拥有的 zlib 代码静态包含到我正在编译的 wasm 模块中?

谢谢。

Ant*_*ris 4

一种方法是在构建过程中包含zlibemcc源。我在下面进行了测试。首先,创建此文件结构(包括您下载的 zlib 源文件夹)

\n\n
$ tree -L 2 .\n.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 build.sh\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 dist\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 lib\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 zlib-1.2.11\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 src\n    \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 decompress.c\n
Run Code Online (Sandbox Code Playgroud)\n\n

构建.sh

\n\n
ZLIB="lib/zlib-1.2.11"\n\nemcc \\\n  -O3 \\\n  -s WASM=1 \\\n  -s EXPORTED_FUNCTIONS="[ \\\n      \'_free\', \'_malloc\' \\\n    , \'_pcf_decompress_zlib\' \\\n  ]" \\\n  -I $ZLIB \\\n  -o dist/decompress.wasm \\\n  $ZLIB/*.c \\\n  src/decompress.c\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

现在,配置 zlib 并构建!

\n\n
$ lib/zlib-1.2.11/configure  `# you only need to run this once`\n$ ./build.sh\n
Run Code Online (Sandbox Code Playgroud)\n