lld不直接控制符号导出,仅依据目标文件中编译时确定的符号绑定属性(如visibility属性、static等)进行链接;导出控制需靠编译期-fvisibility=hidden配合版本脚本--version-script实现。

lld 不直接控制符号导出,靠链接时传入的可见性或版本脚本
lld 本身不提供类似 GCC 的 -fvisibility=hidden 这类编译期开关,它只负责链接阶段的符号解析与重定位。导出哪些符号,本质上由目标文件(.o)里已有的符号绑定属性决定——而这个属性是在编译时(用 clang 或 gcc)通过 visibility 属性、static 修饰、匿名命名空间等手段固化下来的。lld 做的是“照单收货”,不是“重新筛选”。
用 --version-script 在链接时强制过滤导出符号
这是 lld(以及 GNU ld)最可靠、最精细的符号导出控制方式,尤其适合需要 ABI 稳定或向后兼容的场景。
- 写一个版本脚本
version.map,例如:
LIB_1.0 {
global:
init;
process;
local:
*;
};
- 链接时显式传入:
wasm-ld --version-script=version.map -o libmy.so my.o(Linux ELF)或wasm-ld --version-script=version.map -o out.wasm input.o(WASM) - lld 会严格按脚本中
global:列出的符号生成动态符号表(.dynsym),其余一律不导出 - 注意:脚本语法必须符合 GNU ld 规范,lld 完全兼容;但不支持 GCC 特有的
extern "C"风格别名写法
clang 编译时加 -fvisibility=hidden 是前提
如果你希望只导出少数几个函数,必须在编译源码时就隐藏默认可见性,否则 --version-script 只能“删”,不能“补”——它无法把本就没进目标文件符号表的函数拉出来导出。
- 正确流程是:
clang++ -fPIC -fvisibility=hidden -c mylib.cpp -o mylib.o - 再在类/函数声明上加
__attribute__((visibility("default")))标记要导出的接口 - 最后用 lld 链接:
lld -shared -o libmy.so mylib.o - 若跳过第一步,所有符号默认是
default,即使写了版本脚本,也得靠local: *手动屏蔽,容易漏
常见错误:符号明明写了 default 却没导出
这通常不是 lld 的问题,而是 clang 编译阶段没生效,或者符号被优化掉了。
- 检查目标文件是否真含该符号:
llvm-readelf -s mylib.o | grep 'init',看类型是否为GLOBAL DEFAULT - 确认没加
-flto或-O2导致内联或丢弃——测试时先用-O0 - 如果是 C++ 成员函数,确保用了
extern "C"或完整修饰名(如_ZN3Foo4initEv)出现在版本脚本里,否则 lld 找不到匹配项 - WASM 目标下,
--export=xxx是快捷方式,但它只导出函数,不处理变量或 C++ 类,且不支持版本化
真正难的不是写脚本,而是让每个 .o 文件里的符号属性在进入 lld 之前就干净、确定、可预测——编译和链接必须当成一个连贯动作来看,不能割裂。











