lld 目前(截至 2026 年)不支持 gnu ld 的 --version-script 选项,因其 elf 后端尚未实现符号版本脚本解析,即使启用 -flavor gnu 也无法识别该参数。

LLD 不支持 --version-script
直接说结论:lld 目前(截至 2026 年)**不支持 GNU ld 的 --version-script 选项**。如果你在 LLD 上直接加 -Wl,--version-script=xxx.map,会报错:unknown argument: --version-script 或类似提示。
这不是配置问题,是功能缺失——LLD 的 ELF 后端尚未实现符号版本脚本解析。即便你用 lld -flavor gnu 模拟 GNU 链接器行为,该选项仍被忽略或拒绝。
- GNU ld 和 gold 支持完整符号版本化(
readelf -V可见版本节点) - LLD 的 ELF 链接器只支持基础符号导出控制(如
--unresolved-symbols=ignore-all),但不处理版本节点定义 - LLD 的 Mach-O(macOS)和 COFF(Windows)后端也无对应机制
想用符号版本,只能切回 GNU ld 或 gold
如果你的项目强依赖符号版本(比如要兼容旧程序、做 ABI 演进、避免“DLL 地狱”),就别在构建链里硬推 LLD。实际做法很简单:
- 保持源码和版本脚本(如
libfoo.map)不变 - 编译时显式指定 GNU ld:
gcc -shared -fPIC foo.c -Wl,--version-script=libfoo.map -o libfoo.so -fuse-ld=bfd - 或用 gold(更快):
-fuse-ld=gold,同样支持--version-script - 验证是否生效:
readelf -Ws libfoo.so | grep '@'应看到add@LIBFOO_1.0这类输出
注意:-fuse-ld= 是 GCC 的链接器选择开关,不是 LLD 自身参数;LLD 无法替代这个环节。
LLD 用户的折中方案:用 visibility + 版本号命名
如果必须用 LLD(比如追求链接速度、统一工具链),又需要某种“版本感”,可退一步做轻量级隔离:
- 用
__attribute__((visibility("hidden")))默认隐藏所有符号,再显式导出带版本后缀的函数名,例如:int add_v1(int a, int b);、int add_v2(int a, int b); - 在头文件中用宏控制可见接口:
#define add add_v2,旧程序仍可编译,但运行时绑定的是新符号 - 配合
ldd和nm -D检查导出列表,确保没意外泄露内部符号 - 缺点:不提供运行时版本自动降级能力,也不兼容 glibc 那套
@版本解析逻辑
为什么 LLD 还不支持?这不是 bug,是取舍
LLD 的设计哲学是“只做必要事”。符号版本化属于高级 ABI 管理机制,在多数现代项目(尤其云原生、容器化场景)中被语义化版本(如 libfoo.so.1.2.0)+ 构建时静态链接策略替代。LLVM 团队优先实现了更通用的优化(如并行链接、快速重定位),而把版本脚本留给 GNU 生态维护。
真正容易被忽略的点是:很多人以为换链接器只是“换个更快的”,却没意识到它可能砍掉关键 ABI 控制能力。一旦你在 CI 中默认用了 -fuse-ld=lld,又依赖符号版本做灰度发布或插件兼容,上线后就会遇到 undefined symbol: func@LIB_1.0 —— 这个错误不会在编译时报,而是在目标机器上首次 dlopen 时才暴露。











