Clang链接器选择逻辑是“首个生效的-fuse-ld=参数决定最终链接器”,后续同名参数被忽略;若-fuse-ld=lld置于-L/-l之后,clang前端可能因--sysroot自动注入默认-fuse-ld=bfd,导致静默使用GNU ld而非lld,引发混用选项等隐蔽错误。

Clang 编译 RISC-V 目标时,默认不调用 lld,必须显式指定 -fuse-ld=lld,且该参数必须出现在所有 -L、-l 之前;否则链接会静默 fallback 到系统 ld 或报错找不到库。
为什么 -fuse-ld=lld 必须放在最前面
Clang 的链接器选择逻辑是“首个生效的 -fuse-ld= 参数决定最终链接器”,后续同名参数会被忽略。若你把 -fuse-ld=lld 放在 -L/path/to/sysroot/lib 后面,而 clang 前端又因 --sysroot 自动注入了系统路径(比如 /usr/lib)并顺带触发了默认 -fuse-ld=bfd,那实际调用的仍是 GNU ld —— 你根本不会收到警告,只会在链接失败时看到类似 ld: unknown option '--gc-sections' 这种混用选项的错误。
实操建议:
- 始终把
-fuse-ld=lld紧跟在clang命令后、任何-target之后立即写出 - 避免依赖
--sysroot让 lld 自动找库:它对--sysroot的支持非常有限,尤其在 RISC-V ELF 场景下基本不解析/path/usr/lib子路径 - 验证是否生效:加
-v参数,看最后一行输出是否为"/path/to/lld" -o program ...,而不是"ld"或"link.exe"
RISC-V 交叉链接必须显式配 -L 库路径
LLD 不像 GNU ld 那样自动从 --sysroot 推导 /usr/lib、/lib 等子目录。即使你传了 --sysroot=/opt/riscv/sysroot,lld 也不会去查 /opt/riscv/sysroot/usr/lib 下的 libc.a,直接报错 error: unable to find library -lc。
常见错误现象:
-
lld: error: unable to find library -lc,但/opt/riscv/sysroot/usr/lib/libc.a确实存在 - 链接成功但运行时报
Illegal instruction:因为链接了 x86_64 的 crt0.o 而非 RISC-V 版本,根源常是-L指向了错误 sysroot 层级
正确做法:
- 显式添加
-L/opt/riscv/sysroot/usr/lib和-L/opt/riscv/sysroot/lib - 对 C++ 项目,额外加
-L/opt/riscv/sysroot/usr/lib/gcc/riscv64-unknown-elf/14.2.0(路径按实际 GCC 版本调整) - 若用
riscv64-unknown-elf-gcc工具链配套的 sysroot,注意其 libc 通常在usr/riscv64-unknown-elf/lib,不是usr/lib
确认 lld 实际调用的是 ELF 后端而非 COFF 或 wasm
LLD 是多后端合一的二进制,但不同目标平台对应不同内部实现。RISC-V Linux(或裸机)目标属于 ELF 格式,必须确保 clang 调用的是 ELF lld,而不是误走 COFF 或 wasm 分支。这取决于 -target 的完整 triplet 和你的 lld 安装方式。
典型 triplet 示例与对应行为:
-
-target riscv64-unknown-elf→ 正确触发 ELF lld(裸机场景常用) -
-target riscv64-linux-gnu→ 也走 ELF lld,但需确保 sysroot 提供 glibc 或 musl 头文件和库 -
-target riscv32-unknown-elf→ 同样是 ELF lld,但某些旧版 lld 对 32 位 RISC-V 支持较弱,建议用 LLVM 15+ - 若误写成
-target riscv64-windows-msvc,clang 会尝试调用lld-link.exe,直接失败
验证方法:
- 运行
clang -target riscv64-unknown-elf -fuse-ld=lld -v dummy.c -o /dev/null 2>&1 | grep ' "/.*lld" ',确认路径中无lld-link或wasm-ld - 检查
lld --version输出是否含ELF字样(如LLD 18.1.0 (compatible with GNU linkers)默认即 ELF)
裸机链接需手动指定入口与脚本,lld 不自动处理 crt
LLD 不像 GCC 工具链那样自动注入 crt0.o、crtn.o 或隐式链接 -lc。在 RISC-V 裸机开发中,你必须自己提供启动代码、内存布局,并禁用默认入口。
关键参数组合:
- 用
--entry=_start显式指定入口符号(不能用main,除非你自己实现了_start并跳转过去) - 用
-nostdlib禁用所有标准库搜索,避免 lld 去找不存在的libc.a - 用
-T linker.ld提供自定义链接脚本,控制.text、.data等段地址(RISC-V 常需指定ENTRY(_start)和SECTIONS { . = 0x80000000; ... }) - 若需 minimal libc 功能(如
memset),手动-lc并确保-L指向riscv64-unknown-elf-newlib提供的 libgloss + libc
漏掉 --entry 的典型表现:lld: error: undefined symbol: _start,即使你 objdump 看到目标文件里有 _start 符号 —— 因为 lld 默认找的是 main,而裸机没有 main 入口约定。

















