在使用 clang 工具链构建 WASM 模块时,我遇到了一个我很难理解的行为。基本上,我有一个非常简单的 C++ 代码(在
test.cc
中):
extern "C" int foo() { return 42; }
我想以此为基础构建一个 WASM 模块,该模块仅导出其中定义的函数。不同的是,我在链接最终的二进制文件之前添加了创建静态库的中间步骤。
阅读完https://lld.llvm.org/WebAssembly.html#exports后,我就是这样做的:
/usr/bin/clang --target=wasm32 -nostdlib -c test.cc -o test.o
/usr/bin/llvm-ar rcsD libtest.a test.o
/usr/bin/wasm-ld -o test.wasm libtest.a --no-entry --no-gc-sections --export-all
但是查看最终结果,我发现那里没有定义
foo
函数:
llvm-objdump -d test.wasm
test.wasm: file format wasm
Disassembly of section CODE:
00000000 <CODE>:
# 1 functions in section.
00000001 <__wasm_call_ctors>:
3: 0b end
同时,如果我使用
--export=foo
而不是 --export-all
,它会按我的预期工作:
llvm-objdump -d test.wasm
test.wasm: file format wasm
Disassembly of section CODE:
00000000 <CODE>:
# 2 functions in section.
00000001 <__wasm_call_ctors>:
3: 0b end
00000004 <foo>:
.local i32
8: 41 2a i32.const 42
a: 21 00 local.set 0
c: 20 00 local.get 0
e: 0f return
f: 0b end
最后,如果我直接使用目标文件而不是先将其打包到静态库中,它也可以按预期工作。这就是我怀疑问题实际上所在的地方。
我可能推测链接器可能有一些逻辑以不同的方式对待静态库,并且仅从明确请求的静态库中导出符号,并且
--export-all
仅适用于保留的符号,而同时,普通的 --export
意味着必须保留该符号。然而,我未能找到上述理论的证实。
有人可以帮助我理解静态库和常规对象文件之间的区别来自哪里吗?因此,为什么
--export-all
不从链接的静态库中导出符号?
这可能是链接器普遍假定的行为,这就是为什么我很难在文档中找到对此的参考?
谢谢你。
FWIW,如果有帮助的话,我也尝试了多种不同的组合:
__attribute__(( export_name("blah-blah") ))
不起作用__attribute__(( visibility("default") ))
与或不使用 --export-dynamic
都不起作用wasm-ld
或通过 clang
包装器会产生相同的行为。我可能推测链接器可能有一些以不同方式对待静态库的逻辑。
没错。
wasm-ld
是 GNU ld
(GNU linux 链接器)的衍生版本,并且具有
关于何时认为链接需要目标文件的基本规则相同。
任何在命令行中显式输入的目标文件都是无条件、静态地需要的 链接到输出图像。
默认情况下,作为静态库的存档成员的目标文件被认为是需要的, 当且仅当它为未解析的符号引用提供至少一个定义 已在链接中较早累积。请参阅 Stackoverflow 静态库标签 wiki。
此默认值可以通过以下方式覆盖:
--whole-archive libtest.a --no-whole-archive
效果:
--whole-archive liba.a [libb.a]... --no-whole-archive
就是让链接器暂停其对于静态库顺序的默认策略
liba.a [libb.a]...
并考虑这些档案中的所有目标文件
无论他们是否解决任何引用,都需要。请参阅wasm-ld --help
。
如果
--whole-archive
未生效,则链接必须在任何对象之前至少消耗一个显式对象文件
静态库中的文件将被视为需要,因为没有未解析的引用可以
累积直到显式输入包含某些未解析引用的目标文件。
您的链接:
`/usr/bin/wasm-ld -o test.wasm libtest.a --no-entry --no-gc-sections --export-all
不会改变这一点。
--export-all
的作用只是迁移所有符号
将输出图像的全局符号表放入其动态符号表中,使得
它们可供动态链接器访问。但由于 libtest.a(test.o)
甚至没有链接
进入图像,foo
首先不会进入全局符号表。
另一方面,您的链接:
`/usr/bin/wasm-ld -o test.wasm libtest.a --no-entry --no-gc-sections --export=foo
确实有效,因为:
$ wasm-ld --help | grep '\--export=<value>'
--export=<value> Force a symbol to be exported
关键词是力量。
--export=foo
的作用是强制链接器假设
在链接消耗任何文件之前以及最后, foo
是一个未解析的引用
如果找到定义,则在动态符号表中输入 foo
。该假设将
诱导链接器认为需要 libtest.a(test.o)
,在您的情况下,该链接相当于:
$ /usr/bin/wasm-ld -o test.wasm libtest.a --no-entry --no-gc-sections --undefined=foo --export-all
您尝试使用:
__attribute__(( export_name("blah-blah") ))
和:
__attribute__(( visibility("default") ))
不成功,因为第一个仅导致应用到的符号由别名表示 图像的动态符号表,第二个仅应用默认的动态可见性 到图像中的符号。如果没有定义,它们都不能使图像中的符号被定义 其中已链接。