编译到目标 WASM 时,使用 Clang 出现未定义符号错误。

3

我已经成功地在浏览器中运行了一些简单的WebAssembly应用程序,并且能够从JavaScript中调用WebAssembly代码。但是当我尝试从C代码调用JavaScript时,无法使代码编译通过。

我正在使用clang 9.0和wasi-libc。我的编译方式如下:

clang --target=wasm32-wasi -Wl,--allow-undefined-file=src/wasm.syms -Wl,--export-all --sysroot wasm-test/wasi-libc/sysroot -O2 -s -o ./public/main.wasm ./src/main.cpp

src/main.cpp:

#include <stdio.h>

void __console_log(int str, int len);

int main()
{
    printf("MAIN!\n");
    return 0;
}

extern "C"
{
    void test()
    {
        char *str = "TEST!";
        __console_log((int)str, 5);
    }

    int test2()
    {
        return 10;
    }
}

wasm.syms:

__console_log

如果我在cpp文件中删除对__console_log的引用,它可以工作,但是如果我添加它,即使我在命令行中有--allow-undefined-file=src/wasm.syms(也尝试了-U=__console_log,同样的错误),我仍然会得到链接错误:wasm-ld: error: /var/folders/vl/4bjdkcfx1gncfx0vzrl0s57c0000gn/T/main-a723f9.o: undefined symbol: __console_log(int, int)。以下是使用-v标志运行时的完整日志:
$ clang --target=wasm32-wasi -Wl,--allow-undefined-file=src/wasm.syms -Wl,--export-all --sysroot /Users/hoff/dev/wasm-test/wasi-libc/sysroot -O2 -s -o ./public/main.wasm ./src/main.cpp -v
clang version 9.0.0 (git://github.com/llvm/llvm-project.git 0399d5a9682b3cef71c653373e38890c63c4c365)
Target: wasm32-unknown-wasi
Thread model: posix
InstalledDir: /Users/hoff/dev/wasm-test/clang/bin
 "/Users/hoff/dev/wasm-test/clang/bin/clang-9" -cc1 -triple wasm32-unknown-wasi -emit-obj -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cpp -mrelocation-model static -mthread-model posix -masm-verbose -mconstructor-aliases -fuse-init-array -target-cpu generic -fvisibility hidden -dwarf-column-info -debugger-tuning=gdb -target-linker-version 512.4 -momit-leaf-frame-pointer -v -resource-dir /Users/hoff/dev/wasm-test/clang/lib/clang/9.0.0 -isysroot /Users/hoff/dev/wasm-test/wasi-libc/sysroot -internal-isystem /Users/hoff/dev/wasm-test/wasi-libc/sysroot/include/wasm32-wasi/c++/v1 -internal-isystem /Users/hoff/dev/wasm-test/wasi-libc/sysroot/include/c++/v1 -internal-isystem /Users/hoff/dev/wasm-test/clang/lib/clang/9.0.0/include -internal-isystem /Users/hoff/dev/wasm-test/wasi-libc/sysroot/include/wasm32-wasi -internal-isystem /Users/hoff/dev/wasm-test/wasi-libc/sysroot/include -O2 -fdeprecated-macro -fdebug-compilation-dir /Users/hoff/dev/wasm-test -ferror-limit 19 -fmessage-length 102 -fobjc-runtime=gnustep -fcxx-exceptions -fexceptions -fno-common -fdiagnostics-show-option -fcolor-diagnostics -vectorize-loops -vectorize-slp -o /var/folders/vl/4bjdkcfx1gncfx0vzrl0s57c0000gn/T/main-a723f9.o -x c++ ./src/main.cpp
clang -cc1 version 9.0.0 based upon LLVM 9.0.0 default target x86_64-apple-darwin17.6.0
ignoring nonexistent directory "/Users/hoff/dev/wasm-test/wasi-libc/sysroot/include/wasm32-wasi/c++/v1"
ignoring nonexistent directory "/Users/hoff/dev/wasm-test/wasi-libc/sysroot/include/c++/v1"
ignoring nonexistent directory "/Users/hoff/dev/wasm-test/wasi-libc/sysroot/include/wasm32-wasi"
#include "..." search starts here:
#include <...> search starts here:
 /Users/hoff/dev/wasm-test/clang/lib/clang/9.0.0/include
 /Users/hoff/dev/wasm-test/wasi-libc/sysroot/include
End of search list.
./src/main.cpp:17:21: warning: ISO C++11 does not allow conversion from string literal to 'char *'
      [-Wwritable-strings]
        char *str = "TEST!";
                    ^
1 warning generated.
 "/Users/hoff/dev/wasm-test/clang/bin/wasm-ld" --strip-all -L/Users/hoff/dev/wasm-test/wasi-libc/sysroot/lib/wasm32-wasi /Users/hoff/dev/wasm-test/wasi-libc/sysroot/lib/wasm32-wasi/crt1.o --allow-undefined-file=src/wasm.syms --export-all /var/folders/vl/4bjdkcfx1gncfx0vzrl0s57c0000gn/T/main-a723f9.o -lc /Users/hoff/dev/wasm-test/clang/lib/clang/9.0.0/lib/wasi/libclang_rt.builtins-wasm32.a -o ./public/main.wasm
wasm-ld: error: /var/folders/vl/4bjdkcfx1gncfx0vzrl0s57c0000gn/T/main-a723f9.o: undefined symbol: __console_log(int, int)
clang-9: error: linker command failed with exit code 1 (use -v to see invocation)

非常感谢您的帮助。

1个回答

1
你还需要将__console_log声明为extern "C"。这个提示在于函数签名包含在错误信息中。lld要求使用C++符号(也许如果它不这样做会更清晰,我不确定)。

哦,我的天啊,那正是它,因为在 extern "C" 之外的名称会被混淆 :facepalm:。 如果您熟悉 wasm,您是否知道 wasi-libc 是否替代了将导入项作为 WebAssembly.instantiate 的第二个参数发送的需要?我的印象是,wasi-libc 旨在尽可能实现 libc api 而无需 OS 调用。为了澄清,我正在尝试在浏览器中运行 wasm 代码,并且我想知道是否有一种方法可以摆脱 wasmer/类似的运行时。 - Hoffmann
你总是需要设置一组导入才能做任何有用的事情。Wasi libc是建立在一组系统调用上的,这些调用需要由主机实现。你不能在纯wasm中实现这样的东西,因为它除了计算之外没有其他功能。 - sbc100
是的,我想那应该是情况,毕竟你需要一些基本功能来自于wasm之外,比如内存分配器和IO。我只是觉得wasmer太大了,它会对包大小产生巨大的影响,使得在非懒加载场景中使用wasm变得困难。 我看到过一些带有自己小型运行时的github仓库,但这需要很多工作和知识才能构建出完全符合我的需求的东西。 有趣的事实是,我正在测试clang/wasi编译成wasm,使用std::cout会将.wasm文件大小增加超过900kb... - Hoffmann
stdio.h printf 只增加了大约10kb。有趣的是,对开发人员而言,wasm 是两种世界中最糟糕的,需要关心捆绑包大小以及疯狂的编译器/链接器工具链。 - Hoffmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接