Rust: 与 LLD 的链接

创建于 2017-02-17  ·  94评论  ·  资料来源: rust-lang/rust

LLVM 4.0 已启用 LLD,但 AFAIK 尚未在所有平台上生产就绪。 我相信我们已经计划很快进行 LLVM 升级以解决 AVR/emscripten 问题,所以现在是时候开始确定我们可能需要做什么来支持它,与我们的相比,它如何影响编译器性能/二进制大小/运行时性能通常的链接器,以及我们可能希望在哪些平台上默认启用它。

当前状态 (2020-04-24) 总结于https://github.com/rust-lang/rust/issues/39915#issuecomment -618726211

A-linkage C-feature-request I-compiletime T-compiler

最有用的评论

这个错误有点混乱,所以这是我对当前情况的快速总结的最佳镜头,对于想要向前推进的人。

什么是 lld

作为 llvm 项目一部分的链接器,出于两个原因,这是可取的:

  • 交叉编译非常友好(因此它强调嵌入式目标)
  • 它非常快(通常运行时间是 Gold 的一半——大型项目(rustc、servo 等)的链接可能需要几分钟时间,并且链接可能是增量构建编译的很大一部分,因此将这个运行时间减半是一个大交易。)

今天 Rust 对 lld 所做的事情

  • Rust 目前在大多数平台上以二进制形式发布自己的 lld 副本,它称为 rust-lld
  • 许多裸机目标默认使用 rust-lld
  • rust-lld 默认用于 wasm
  • (?) 您可以通过使用“-C linker-flavor”明确请求使用 rust-lld(不清楚这在非裸机平台上究竟做了什么,见下文)

更多地方使用rust-lld的问题(即桌面linux/mac/windows)

  • 用于 lld 的 macOS (Mach-O) 后端已损坏并废弃

    • 从头开始重写,但还为时尚早

  • 在 linux/unix 平台上,您不应该直接调用 ld/lld。 你应该通过你的系统 c 编译器(即 gcc)调用链接器,它的职责是发现像 crt1.o 这样的系统符号并将它们提供给 ld。 这意味着我们不能“仅仅”使用 rust-lld; 我们必须将它输入到 gcc/clang/whatever 中。 (我们不想自己实现这个系统符号逻辑)

    • 通常,您不能将链接器作为路径提供,您必须将其作为“ld”注入到 C 编译器的搜索路径中

    • 或者,您可以做同样的事情,但将其注入为“ld.lld”,并通过“-fuse-ld=lld”



      • 这可能很重要,显然 lld 会检测它是作为“ld”还是“ld.lld”执行的(需要调查)


      • 不幸的是 -fuse-ld=lld 只是 GCC 9 的一部分,所以我们可能需要功能/版本检测才能使用它(clang 已经有很长时间了)



  • windows-msvc显然状况良好,似乎对在后端使用 rust-lld 的支持有限,但我不清楚这里需要做什么。
  • windows-mingw 似乎与 linux/unix 大致相同,除了您可能会得到一个古老的 GCC,而且事情有点不稳定,因为pseudo-windows-linux 并不是经过充分测试的配置?

此外,一般来说,lld 较新,它不是大多数操作系统的默认设置,如果我们开始在更多地方使用它,几乎肯定会出现随机兼容错误。

我已经提交了两个元错误,以集中精力在两个平台上默认使用 (rust-)lld:

  • #71515 - x64 Ubuntu 20.04 LTS(以及更广泛的所有 x64 ELF 平台)
  • #71520 - x64 msvc 窗口

所有94条评论

另见#36120 中的 PoC。

LLD 可能是 MinGW 目标的一个很好的候选者,因为我们目前无论如何都将链接器与它们捆绑在一起,而 MinGW 的链接器有各种各样的问题,从缺乏 ASLR 到没有 bigobj 支持。 如果我们可以在交叉编译时以某种方式带来必要的 mingw 库,而不仅仅是本机目标(目前仅限于 rustup 的 mingw 包),那么这将启用 rust 开箱即用的 linux 交叉编译,这将是一个巨大的改进人们从他们的发行版中获得 MinGW 然后遇到问题的现有情况,因为发行版几乎总是使用不兼容的 MinGW。

由于多种原因,LLD不是本地定位 MSVC 目标的良好候选者,主要原因是缺乏 debuginfo 支持。 交叉编译到 MSVC 目标需要无法重新分发的库,因此无论如何我们都无法支持开箱即用。

升级到 LLVM 4.0 的跟踪问题是https://github.com/rust-lang/rust/issues/37609

根据记录,lld 绝对没有为 Solaris 目标做好准备。 但是在 Solaris 上,我没有理由知道要使用 lld 而不是本机 ld。 我们已经在研究如何让 rust 在 Solaris 上使用 Solaris ld 而不是使用 gcc 进行链接。

@binarycrusader使用 lld 的一个原因是在Solaris 而不是Solaris

PR rust-lang/rust#40018 向 rustc 添加了一个-Z linker-flavor标志,以便可以使用 LLD 作为链接器。 该 PR 没有将 LLD 嵌入 rustc 中,但允许对其进行树外实验。

@binarycrusader ^ 可能有助于您直接使用 Solaris 的 ld 而不是 gcc 的实验。

我们现在似乎在 LLVM 4.0 上运行。 @japaric ,这是否意味着现在可以轻松地使用链接器风格标志来比较和对比 LLD 与系统链接器?

@bstrie #40018 几周前登陆。 自从那个登陆的人已经能够使用-Z linker-flavor=ld -C linker=ld.lld来使用外部 LLD 二进制文件作为链接器。 请注意,与 gcc 不同,LLD 不知道系统库的位置,因此如果您要链接到任何系统库,则必须使用-C link-args='-L ...'将库搜索路径传递给链接器。

LLVM 4.0 的帮助是将 LLD 合并到 rustc 中。 通过这种变化,我们在某些情况下不需要外部链接器,比如链接 MUSL 二进制文件或裸机程序。 我说一些场景是因为大多数目标都需要链接到系统库,在那里你会遇到我上面提到的库搜索路径问题。 对于这些目标,LLD 将无法开箱即用。 目前尚不清楚如何以及在何处解决该问题,如果没有解决方案,我们就无法为最重要的(第 1 层)目标切换到 LLD,这首先降低了将 LLD 嵌入到 rustc 中的吸引力。

@japaric反对将(sysroot 相关库)搜索路径以及诸如-lc -lpthread crt0.o直接嵌入 rustc 的论据是什么? 毕竟,工具链的某些组件必须嵌入它们,因为我们没有任何平台可以遵循的标准,而 binutils 不是这些知识的黄金来源。

我能想到的唯一缺点是相同的三元组在不同风格的系统上会有不同的搜索路径(这可能是 Linux/glibc 三元组独有的,在具有 multilib 的平台上尤其糟糕)。 在这种情况下,我相信 clang 会窥探操作系统名称并硬编码操作系统特定的约定,这看起来很糟糕,但如果想要分发在任何 Linux 上运行的单个二进制文件(并且不需要系统链接器),这可能是不可避免的。

@retep998几个月前,我对 lld 进行了简要介绍。 我无法交叉编译(交叉链接?)来自 Linux 的 .exe。 看起来 lld 只支持平台原生格式。

希望我错了。

将此标记为性能错误,因为根据 LLD 的基准测试,它的性能似乎比 GNU ld 高 10 倍,并且链接性能目前是编译器速度的一个重要组成部分。

呃,忘了链接基准测试: https :

(今天相关,因为 LLVM 5.0 刚刚发布。)

使用 LLD 链接比 bfd 或 gold 快得多,但我怀疑使用它是否会显着提高整体性能。 我仍然认为这个问题很重要,应该是一个优先事项。

@tpimh我实际上并不完全确定 I-slow 标签是否应该代表运行时性能错误或编译时性能错误,我打算将它作为后者。 当我查看时间传递输出链接时,IME 通常处于前三个最长的阶段,比大多数阶段要长得多,因此即使将链接时间减少一半也可能是一个巨大的胜利(特别是对于像 Servo 和 rustc 这样的大事)。

@bstrie I-slow 是针对糟糕的运行时性能,I-compiletime 是针对我上次检查时的编译器性能

对于那些对从 Linux 到 Windows 的交叉链接这个晦涩的话题感兴趣的人来说,这是个好消息。 我之前说过 lld 是不可能的,但这仅适用于 lld 的 ld 风味。 lld 的 link.exe 风格(lld-link)是可能的。

特别是对于 Rust,我们今天可以通过一些代码更改来做到这一点。

  1. 我们需要将 mingw-w64 的 CRT 的一个很小的子集编译成 .o 目标文件。 即一些线程本地存储初始化。 我们还需要 chkstk。

  2. lld 不喜欢 MinGW 通常的导入库。 相反,我们需要自己将 .def 文件构建到 .lib 文件中,使用 lld-link 或 llvm-dlltool

  3. 修改 lld 以将 IMPORT_NAME_NOPREFIX 视为
    IMPORT_NAME_UNDECORATE,因为即使使用第 2 步,.libs 也不完美

  4. 修改 Rust 的 seh.rs 用 ptr::null() 替换 TYPE_INFO_VTABLE。 必需,因为符号??_7type_info@@6B@未在 MinGW 中定义。 然后构建并安装 Rust。

  5. 使用 .cargo/config 将自定义包装器脚本指定为链接器。

  6. 我们的包装器链接器脚本应该主要使用它传递的参数来调用 lld-link。 但是,我们必须进行一些调整:

    a) 修复文件名大小写,例如将 AdvAPI32.Lib 更改为 advapi32.lib

    b) 修改 Rust 生成的 .def 文件,用额外的下划线前缀符号

    c) 覆盖入口点 (/entry)。 可能由于名称修改问题而需要。

    d) 附加您在步骤 1 中编译的 mingw-crt 目标文件

  7. 使用 xargo --target=i686-pc-windows-msvc 构建你的 Rust 项目

执行上述步骤允许我交叉编译 Rust 代码。 我什至可以使用 Rust 的基于 SEH 的展开来恐慌和捕捉恐慌。

@iainnicol您将 msvc 目标与 MinGW 位混合在一起,这就是您必须进行所有这些奇怪修改的原因。 如果您只是从现有的 VC++ 安装中复制库,那么您可以正常使用 lld-link,而无需进行所有这些修改或任何 MinGW 位。

但我不想使用现有的 VC++ 安装。 甚至没有任何方法可以在不花费八个小时下载和安装垃圾的情况下获得一个,更不用说重新分发了。

独立构建工具的重量要轻得多,除非这已经是您所指的,在这种情况下,也许我们应该做一些工作来改进或重新创建 MinGW 所做的事情,以便它实际上与 MSVC 兼容。

独立构建工具的重量要轻得多

我还没有意识到微软分发了这些。 你能链接到他们吗? 是否有任何合理的方法可以在不实际运行的情况下提取安装存档,即它是 msi 还是类似的东西?

它们是: http :

2015 和 2017 版本都是 exe,但您可以通过以下方式说服 2017 exe 为您提供您想要的: https :

如果我们真的想在 Windows 上正确地做到这一点,我们首先需要https://github.com/rust-lang/rust/issues/30027来消除对 Windows SDK 或 MinGW 的导入库的需要。 然后我们剩下的就是用我们自己的纯 Rust 版本(数学/内存函数、入口点、Rust 需要的其他一些运行时位)替换 CRT 位,我们将拥有一个完全独立的 Rust 工具链,可以创建 Windows 二进制文件! 这样做的缺点是您将无法静态链接 C/C++ 代码,因为这在很大程度上依赖于从 MinGW 或 VC++ 链接到适当的 CRT。 当然,Rust 的全部意义在于用 Rust 重写所有内容,所以这并不是什么大问题。

对于那些对从 Linux 到 Windows 的交叉链接这个晦涩的话题感兴趣的人来说,这是个好消息。 我之前说过 lld 是不可能的,但这仅适用于 lld 的 ld 风味。 lld 的 link.exe 风格(lld-link)是可能的。

看起来现在应该也可以使用 ld 风格: https :

新的 lld 的 MinGW 兼容驱动程序是 lld-link 链接器的包装器。 它在内部将 Unix-ish 选项转换为 Windows-ish 选项,然后调用 lld-link 的入口点。 我不确定你们是否想使用它,因为(除了包装器驱动程序不完整且尚未准备好使用)它似乎并没有让事情变得更容易,除非您已经拥有 MinGW 的 Makefile。

我有一个关于交叉编译的(可能是愚蠢的)问题要问你们。 在 Windows 上,所有 dllimport 的符号都有导入它们的 DLL 名称。 如果您没有任何 MSVC 库文件,您怎么知道 dllimport 的符号是从哪些文件中导入的?

我有一个关于交叉编译的(可能是愚蠢的)问题要问你们。 在 Windows 上,所有 dllimport 的符号都有导入它们的 DLL 名称。 如果您没有任何 MSVC 库文件,您怎么知道 dllimport 的符号是从哪些文件中导入的?

如果您没有任何导入库,那么您必须创建导入库或实现https://github.com/rust-lang/rust/issues/30027以便winapi可以完成所有困难指定每个符号来自哪个 DLL 以及像序数这样的滑稽动作的工作。 事情已经在链接时符号/序号在DLL文件来指定符号的映射,无论是在你的代码导入库或注释。

拉入https://reviews.llvm.org/rL311734后,我几乎可以在 macOS 上使用 lld 引导 rustc。 dylib 元数据似乎存在问题,我仍然需要对此进行调查。

我有一个分支可以复活https://github.com/rust-lang/rust/pull/36120; 由于我们需要(最近的)lld 修复,因此在https://github.com/rust-lang/rust/issues/43370上被阻止

@tamird :#43370 已关闭。

LLD 已添加到https://github.com/rust-lang/rust/pull/48125 中,现在随第 1 层平台(mac、linux、windows)一起提供。 您可以针对每个平台使用-Z linker-flavor进行测试,尽管默认情况下它不太可能适用于大多数平台。 但是,默认情况下,它在 MSVC 上确实有效。 例如:

$ RUSTFLAGS='-Z linker-flavor=lld-link' cargo build

将 Cargo 自身的链接时间从 2.5 秒减少到 1.5 秒,一个不错的改进!

@alexcrichton ,下一步是什么? 理想情况下,我们让 LLD 在所有平台上默认工作(我不知道这将需要多少工作),然后我想运行编译时/运行时基准测试以查看将 LLD 设为默认值是否有意义任何平台。 特别是对于增量编译,链接性能将比以往任何时候都更加重要。

特别是对于增量编译,链接性能将比以往任何时候都更加重要。

遗憾的是,链接性能仍然不够重要,我们无法在支持它的平台上启用增量链接。 https://github.com/rust-lang/rust/issues/37543

@bstrie我想这些是下一步,让它在其他平台上工作:)

至于这意味着什么,我不确定,但它已经在 MSVC 上工作了,我认为它离在 MinGW/Linux 上工作还很远,而且我们在 OSX 上非常接近。 至于跨架构支持,我也不确定。 我不希望在不久的将来我们会为除了 wasm 平台之外的任何东西“稳定”它。

@alexcrichton请问您如何指定“稳定”? 为什么不能“稳定”与 lld 的链接以用于其他主要平台 rust 支持? (例如,从 Linux 为 macOS 交叉编译可执行文件)。

现在,交叉编译非常痛苦,例如从 linux 交叉编译适用于 macOS (x86_64-apple-darwin) 的可执行文件所需的工作需要非常重要的步骤,例如获取 xcode sdk 和构建整个工具链。

@cynecx一个好问题! 一个我没有想太多。 但是,我认为,我们不想仅仅因为我们为另一个平台添加了 LLD 就事实上稳定了 LLD,这需要时间和工作才能使其正确并很好地公开它。

例如,从 linux 交叉编译适用于 macOS (x86_64-apple-darwin) 的可执行文件所需的工作需要非平凡的步骤,例如获取 xcode sdk 和构建整个工具链。

LLD 在这里并没有真正的帮助,您仍然需要 Xcode SDK,因为它具有您无法重新分发的标头(并且根据您正在构建的内容,您还需要其他 SDK 工具)。

现在每晚内置 LLD 的真正好处是,您可以使用RUSTFLAGS='-Z linker-flavor=ld.lld' cargo build --target x86_64-unknown-linux-musl轻松地将纯 Rust 项目从 Windows 交叉编译到 Linux。 非常适合为不能简单地安装 Rust 的 Linux 机器编写小工具。

我不希望在不久的将来我们会为除了 wasm 平台之外的任何东西“稳定”它。

就像@rkarp所说的那样,一个非常常见的用例是针对 x86_64-unknown-linux-musl(最终是 steed)来支持容器化的 Linux 工作负载。 这是 Go 做得非常好的事情之一,我们似乎非常接近 Rust 也能够做到这一点。 就实际使用而言,我敢打赌 x86_64-unknown-linux-musl 的 LLD 实际上会比 wasm 得到更广泛的使用。

更一般地说,当涉及到交叉构建时,我认为“它必须适用于所有主机和/或所有目标”的方法没有意义。 我认为随着目标开始工作,逐个目标地稳定这一点是有意义的。

特别是,我很乐意帮助尽快稳定 x86_64-unknown-linux-musl 目标的 LLD。

我的项目有 37 个板条箱,构建链接了大约 70 个二进制文件(大量测试)。 不科学地(盯着top )至少有一半的构建时间我们只运行 ld。 我希望使用 lld 会大大加快我们的构建速度。 我们在稳定的 Rust 上,我还没有设法让 lld 6.0 工作。

@briansmith-Z linker-flavor=ld.lld来测试它,如果这似乎可行,我们可以切换默认值!

@rocallahan只是为了确认,你们目前都在使用黄金链接器,对吗? (因为 afaik 它比标准的 binutils 链接器快)。 如果-Z linker-flavor=ld.lld有效(并且速度更快),那么我们或许可以寻求稳定它! 那是在什么平台上的?

不科学地(引人注目)至少有一半的构建时间我们只运行 ld。

那是用于调试版本 BTW。

你们目前都在使用黄金链接器,对吗? (因为 afaik 它比标准的 binutils 链接器快)

不,那是 Fedora 系统链接器,标准的 GNU 链接器。

那是在什么平台上的?

Fedora 27,带 SSD 的四核 Skylake 笔记本电脑。 我会得到一些性能数据。

啊,很高兴知道! 调试构建可能会从 split dwarf (https://github.com/rust-lang/rust/issues/34651) 而不是链接器改进中获得最大的链接时间。

但是,对于时间信息, @rocallahan是否有机会使用ld.goldld.lld头脑测试?

当然。 我还应该提醒一下,问题 #48762 对于加快 Linux 调试链接时间来说是非常容易实现的。 (我们已经在使用一个被黑的链接器脚本,它会从可执行文件中删除.debug_pubnames / .debug_pubtypes 。)

拆分 DWARF 可能很好,但也可能会给用户带来问题。 我会在那个问题上发表评论。

@briansmith

好的,我会测试的。 也许最初应该在“默认”和“仅在夜间”之间有一个中间立场,某种方式选择使用 LLD,就像我们可以使用-Z ,但不使用-Z以使其有效在稳定版本中。

但不使用 -Z 以使其在稳定版本中工作。

你可以试试RUSTC_BOOTSTRAP=1 RUSTFLAGS="-Z linker-flavor=foo" cargo build

请让我们不要推荐引导标志? 我担心如果有足够多的项目依赖它,事情会变得事实上稳定,从而破坏整个稳定机制的意义。

对不起=/

作为一个数据点,在 Rust 存储库中链接rustc_trans板条箱本身从 78 秒的链接时间下降到我本地机器上的 1 秒链接时间。

我的表现是由于在我们的 crate 层次结构的底部附近对 crate 进行了空白更改。 四核 Skylake 笔记本电脑,16GB RAM,rustc 1.24.0,LLD 7.0.0,GNU ld 2.29-13。 我们使用一个自定义的链接器脚本来丢弃.debug_pubnames.debug_pubtypes ; 当存在链接描述文件时,LLD 使用完全不同的代码路径,因此这可能会影响事情。

GNU ld:

real    2m39.138s
user    8m18.992s
sys 1m37.513s

法学士:

real    2m19.164s
user    6m4.477s
sys 0m56.858s

Gold 没有用,它在我们的链接器脚本上发出了警告。 结果相当稳定。 LLD 对端到端时间的影响不大,但确实显着降低了 CPU 使用率; 我想这意味着我们的构建不会花费太多时间等待 lds 完成,但它确实花费了大量 CPU 时间来运行它们。

有关真实示例,请参阅 #50584,其中从 GNU ld 切换到 lld 使常见的“小幅更改和重建”工作负载的执行速度提高 2.5 倍以上。

https://github.com/rust-lang/rust/issues/50584#issuecomment -400918647 这里更合适:


稳定 LLD 的下一步是获得一个标志,如 -Z linker-flavor=lld,适用于所有目标(Windows + Mac + Linux)。 它会做任何它需要做的事情来跨各种平台工作。

完成后,我们可以向社区做广告,征求反馈意见。 在这里,我们可以获得时间信息以及发送给 LLD 的错误报告。 如果一切顺利(对于全新的链接器,这有点令人怀疑,但嘿,你永远不知道!)我们可以默认打开它,否则我们可以努力稳定 LLD 的选择,然后向 Cargo 添加一个选项。 toml 所以项目至少可以选择加入它。

我们已经为一些目标切换到 lld: https :

我也相信wasm?

此问题是否涵盖与外部 lld 二进制文件的链接以及与 rustc 本身内置的内部 lld 支持的链接? 还是只有前者?

此问题是否涵盖与外部 lld 二进制文件的链接以及与 rustc 本身内置的内部 lld 支持的链接? 还是只有前者?

只是外部 lld 二进制文件,IIUC。

@nnethercote是否还有其他问题可用于跟踪内部链接器的使用,还是应该为此单独提交问题?

我以前没有听说过内部链接器的想法。 我不知道它的 PR。

https://github.com/rust-lang/rust/pull/57514为使用 LLD 链接 LLVM 奠定了基础。

也许最初应该在“默认”和“仅在夜间”之间有一个中间立场,某种方式选择使用 LLD,就像我们可以使用 -Z 一样,但不使用 -Z 以便它在稳定版本中工作。

https://github.com/rust-lang/rust/pull/56351添加了-C linker-flavor

目前尚不清楚此问题旨在跟踪什么。 似乎最好关闭它以解决特定问题,例如“当 Microsoft 的工具链不可用时,为 -msvc 目标链接 LLD”。

对我来说,这个问题是关于启用 LLD 作为所有目标的默认链接器。 我希望这样,因为 LLD 非常快,链接时间通常是编译时间的重要组成部分,编译速度是一个长期存在的问题。

FWIW 我提交了一个错误,用于在 BMO 中LLD 。 显然那是 WONTFIX。 从那里的评论看来,它似乎不像“LLD 非常快”那么简单,因为不同平台上的 LLD 是不同的程序,而 macOS 上的程序因开发停滞而被打破。

@briansmith 一致认为,最好有专门的问题来跟踪不同目标的状态,但与其关闭它,我们还可以将其转变为元错误。 如果有人更了解哪些目标值得打开问题,请随意,因为我对 LLD 支持状态非常不了解。

是否在任何地方记录了与 LLD 的链接? 我有(在 Linux 上) rustc -C linker-flavor=ld.lld hello.rs ,但我没有运气。 我认为 LLD 与我们的 LLVM 副本一起分发,我错了吗? 我也试过通过 apt 安装 LLD,但 rustc 仍然很困惑。 今天使用 Rust 代码尝试 LLD 必须采取哪些步骤?

@bstrie您必须另外传递-C linker=rust-lld参数。

它应该与货物一起工作吗? 我目前在尝试在最新的每晚生成的 Rust 和 Cargo 上构建空白项目时遇到以下错误。

$ RUSTFLAGS='-C linker=rust-lld' cargo build
   Compiling rust3 v0.1.0 (/home/carado/tmp/rust3)
error: linking with `rust-lld` failed: exit code: 1
  |
  = note: "rust-lld" "-flavor" "gnu" "-L" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.2ualxzb8lqn4ho3y.rcgu.o" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.32vfyq64cfbzv618.rcgu.o" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.4rbt3m5y8o8cl09t.rcgu.o" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.ben0932xzwyt64v.rcgu.o" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.fzsdnygvstiwzxo.rcgu.o" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.x0rq6ifodcf11zi.rcgu.o" "-o" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55" "/home/carado/tmp/rust3/target/debug/deps/rust3-c4f8c40972021c55.1m259ox4uzrzk583.rcgu.o" "--gc-sections" "-pie" "-zrelro" "-znow" "-L" "/home/carado/tmp/rust3/target/debug/deps" "-L" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "--start-group" "-Bstatic" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-44988553032616b2.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-607feef6be9150b2.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libbacktrace-a8dbf6d92401e34a.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libbacktrace_sys-9a4716f5e8a3e722.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-988a64d96b043c6d.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-cadd6177b8c6d586.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-8f1d8efc92b45369.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-1e76014677816767.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-cc28bce38cb195d9.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-4123e9e89add689a.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-4d259c17788c1fb5.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-9495dbda85bb8f16.rlib" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-793d0026c575805f.rlib" "--end-group" "/home/carado/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-33c3162edae6574e.rlib" "-Bdynamic" "-ldl" "-lrt" "-lpthread" "-lgcc_s" "-lc" "-lm" "-lrt" "-lpthread" "-lutil" "-lutil"
  = note: rust-lld: error: unable to find library -ldl
          rust-lld: error: unable to find library -lrt
          rust-lld: error: unable to find library -lpthread
          rust-lld: error: unable to find library -lgcc_s
          rust-lld: error: unable to find library -lc
          rust-lld: error: unable to find library -lm
          rust-lld: error: unable to find library -lrt
          rust-lld: error: unable to find library -lpthread
          rust-lld: error: unable to find library -lutil
          rust-lld: error: unable to find library -lutil


error: aborting due to previous error

error: Could not compile `rust3`.

To learn more, run the command again with --verbose.

我遇到了与 carado 相同的错误。 设法“shoehorn” -L /usr/lib 进入链接器调用,但这只是将丢失的库列表缩短为-lgcc ,它在系统中的任何地方都不存在libgcc (有一个libgcc_s.a ) 我怀疑这是一些 gnu-ism 的结果,但无法弄清楚如何解决它。

@almindor尝试RUSTFLAGS='-C linker=rust-lld -L /usr/lib -L /usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0'或类似的东西。 该路径将取决于您的发行版和编译器版本。

我上面的评论是使用 LLD 的正确方法吗? 我无法让它工作,因为每个程序都以SIGSEGV崩溃:

Reading symbols from target/debug/hello...
(gdb) show directories
Source directories searched: ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/etc:$cdir:$cwd
(gdb) r
Starting program: target/debug/hello 

Program received signal SIGSEGV, Segmentation fault.
core::ops::function::FnOnce::call_once{{vtable-shim}} () at /rustc/a7f28678bbf4e16893bb6a718e427504167a9494/src/libcore/ops/function.rs:231
(gdb) l
226     #[stable(feature = "fn_once_output", since = "1.12.0")]
227     type Output;
228 
229     /// Performs the call operation.
230     #[unstable(feature = "fn_traits", issue = "29625")]
231     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
232 }
233 
234 mod impls {
235     #[stable(feature = "rust1", since = "1.0.0")] 
(gdb) info reg
rax            0x0                 0
rbx            0x0                 0
rcx            0x0                 0
rdx            0x0                 0
rsi            0x0                 0
rdi            0x0                 0
rbp            0x0                 0x0
rsp            0x7fffffffddb0      0x7fffffffddb0
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0x0                 0
r12            0x0                 0
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
rip            0x7ffff7ffc000      0x7ffff7ffc000 <core::ops::function::FnOnce::call_once{{vtable-shim}}>
eflags         0x10202             [ IF RF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) disassemble 
Dump of assembler code for function core::ops::function::FnOnce::call_once{{vtable-shim}}:
=> 0x00007ffff7ffc000 <+0>: mov    (%rdi),%rax
   0x00007ffff7ffc003 <+3>: mov    (%rax),%rdi
   0x00007ffff7ffc006 <+6>: jmpq   *0x11d4(%rip)        # 0x7ffff7ffd1e0
End of assembler dump.

对于在这里结束的任何人来说,如果您使用 GCC 9 或 Clang 作为编译器,那么神奇的咒语是RUSTFLAGS="-C link-arg=-fuse-ld=lld" cargo build 。 或者,无论 GCC 版本如何, -C linker=clang都应该可以工作,因此它可能是首选。

要使其永久化,您可以将其添加到特定项目中的~/.cargo/config.cargo/config中:

[build]
rustflags = ["-C", "linker=clang"]
# rustflags = ["-C", "link-arg=-fuse-ld=lld"]

@lnicola注意它仅在使用 GCC 9 或 Clang 作为 CC 时才有效。

@bstrie你知道这个的当前状态是什么吗? 推进它的障碍是什么?

@mati865您是否碰巧知道对 GCC 较旧的人的替代调用?

@lnicola我所有的平台都有 Clang + GCC 9,我还没有研究如何在不兼容的编译器中使用它。

@jonhoo我不了解这方面的任何工作,我想您想问问编译器团队。

我不认为我可以标记团队,我也不想给他们造成不必要的噪音。 您认为让某人从那里快速浏览的最佳途径是什么?

分流; @rust-lang/compiler 有谁知道这个问题的当前状态是什么?

对于这里设法让 LLD 与 Rust 一起工作的任何人,您能否另外包括有关您的平台的详细信息以及正在使用的所有编译器的特定版本? 我仍然看到人们在使用它时遇到麻烦,即使这里列出了建议。

我上面发布的命令适用于带有 GCC 9.2.0 和 LLD 9.0.0 的 Linux。 我认为它有时也适用于 Windows,但我见过有人使用 GCC 9 for Windows 不支持 -fuse=lld。 根据此处发布的一些链接,在 MacOS 上不值得尝试。

对于这里设法让 LLD 与 Rust 一起工作的任何人,您能否另外包括有关您的平台的详细信息以及正在使用的所有编译器的特定版本? 我仍然看到人们在使用它时遇到麻烦,即使这里列出了建议。

cat /etc/system-release
Fedora 发布 30(三十)

cc --version
cc (GCC) 9.2.1 20190827(红帽 9.2.1-1)

ld.lld --version
LLD 8.0.0(与 GNU 链接器兼容)

希望这会有所帮助

但我看到有人使用 GCC 9 for Windows 不支持 -fuse=lld

@lnicola
Windows GCC 9 构建支持-fuse-ld=lld (除非它们被修补为不支持它,但为什么有人会这样做?)。
我想rust-mingw组件已安装并且链接器没有在.cargo/config被覆盖。 这样 rustc 选择了它提供的 GCC 6 而不是系统 1。

Windows 上的另一个问题是硬编码的链接器标志--enable-long-section-names ,LLD 9 和更早的版本不支持它(有计划在未来支持它)。 要解决此问题,您可以:

  • 创建剥离此标志的包装器
  • 修补 LLD 以接受此标志为无操作
  • 使用不使用此标志的本地修补 Rust 构建

Windows 上的另一个问题是硬编码的链接器标志 --enable-long-section-names,LLD 9 和更早的版本不支持它(有计划在未来支持它)。

这部分由: https :
Windows-gnu 用户仍然需要手动工作才能使用支持-fuse-ld=lld C 编译器。

@bstrie :这适用于 Windows-MSVC 上的 Stable 和 Nightly,详细信息请参见本 gamedev-wg 问题的第一篇文章: https :

另一个数据点:在构建rustc本身时使用RUSTFLAGS="-C link-arg=-fuse-ld=lld" ,在我的快速 14 核 Linux 机器上,链接时间从 93 秒下降到 41 秒。

@nnethercote:是从设置不同linker=lld在(例如)的[target.x86_64-unknown-linux-gnu]的第config.toml

@Aaron1011 :我的猜测是这两种方法具有相同的效果,但我自己没有检查过。

@Aaron1011应该是叮当声,参见https://github.com/rust-lang/rust/issues/39915#issuecomment -538049306。

@mati865
您是否尝试过使用 LLD 作为链接器在x86_64-pc-windows-gnu上构建rustc

我今天试过了,LLD 要么挂在构建中间,停止做任何工作,要么抱怨unknown argument: --version-script=...
如果 LLD 仅用于链接 LLVM,也会发生挂起,使用

[llvm]
use-linker = "lld"

工具版本:

$ ld.lld --version
LLD 9.0.1 (https://github.com/msys2/MINGW-packages.git 5e3b8820ed9f04221affee4197e458aca2612e87) (compatible with GNU linkers)

$ gcc --version
gcc.exe (Rev2, Built by MSYS2 project) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

@petrochenkov是的,我可以通过一些技巧来构建它:

  • LLD COFF 后端不支持链接器脚本,但 LD 和 LLD 都适用于 MSVC 样式的.def文件; 容易解决
  • LLD 期望库以lib开头(这是 UNIX 世界的标准)但不知何故 Rust 最终没有为所有windows-*目标这样做: https :
    LD 不介意任何一种方式,我不知道是否推动 LLD 或 Rust 方面来修复它。

我在这里写下一些未来的可能性。 虽然需要一些实施工作,但我认为这些将有助于解决 Rust 开发中一些长期存在的痛点:

  • 与其使用外部提供的 lld,不如在 rustc 中构建和使用它作为静态库。 这允许更紧密地集成到 rustc 本身,改善开箱即用的体验,并开启未来的可能性。
  • 不要将大量路径名作为链接器命令传递给 lld,而是使用虚拟文件系统,并将这些数据从内存传递到内存,而不是在有足够的内存空间时使用磁盘 IO。 对于较大的项目,这可能会节省数百甚至数千兆字节的磁盘 IO,从而缩短编译时间。

不要将大量路径名作为链接器命令传递给 lld,而是使用虚拟文件系统,并将这些数据从内存传递到内存,而不是在有足够的内存空间时使用磁盘 IO。 对于较大的项目,这可能会节省数百甚至数千兆字节的磁盘 IO,从而缩短编译时间。

这不仅仅是IO带宽的问题。 在像 Windows 这样的平台上,尤其是当启用了 Windows Defender 时,你想要处理的每个文件都会增加相当大的时间损失,而 Rust 的代码生成单元模型意味着 crate 被分成数百个可以快速完成大量编译的小目标文件时间。

这个错误有点混乱,所以这是我对当前情况的快速总结的最佳镜头,对于想要向前推进的人。

什么是 lld

作为 llvm 项目一部分的链接器,出于两个原因,这是可取的:

  • 交叉编译非常友好(因此它强调嵌入式目标)
  • 它非常快(通常运行时间是 Gold 的一半——大型项目(rustc、servo 等)的链接可能需要几分钟时间,并且链接可能是增量构建编译的很大一部分,因此将这个运行时间减半是一个大交易。)

今天 Rust 对 lld 所做的事情

  • Rust 目前在大多数平台上以二进制形式发布自己的 lld 副本,它称为 rust-lld
  • 许多裸机目标默认使用 rust-lld
  • rust-lld 默认用于 wasm
  • (?) 您可以通过使用“-C linker-flavor”明确请求使用 rust-lld(不清楚这在非裸机平台上究竟做了什么,见下文)

更多地方使用rust-lld的问题(即桌面linux/mac/windows)

  • 用于 lld 的 macOS (Mach-O) 后端已损坏并废弃

    • 从头开始重写,但还为时尚早

  • 在 linux/unix 平台上,您不应该直接调用 ld/lld。 你应该通过你的系统 c 编译器(即 gcc)调用链接器,它的职责是发现像 crt1.o 这样的系统符号并将它们提供给 ld。 这意味着我们不能“仅仅”使用 rust-lld; 我们必须将它输入到 gcc/clang/whatever 中。 (我们不想自己实现这个系统符号逻辑)

    • 通常,您不能将链接器作为路径提供,您必须将其作为“ld”注入到 C 编译器的搜索路径中

    • 或者,您可以做同样的事情,但将其注入为“ld.lld”,并通过“-fuse-ld=lld”



      • 这可能很重要,显然 lld 会检测它是作为“ld”还是“ld.lld”执行的(需要调查)


      • 不幸的是 -fuse-ld=lld 只是 GCC 9 的一部分,所以我们可能需要功能/版本检测才能使用它(clang 已经有很长时间了)



  • windows-msvc显然状况良好,似乎对在后端使用 rust-lld 的支持有限,但我不清楚这里需要做什么。
  • windows-mingw 似乎与 linux/unix 大致相同,除了您可能会得到一个古老的 GCC,而且事情有点不稳定,因为pseudo-windows-linux 并不是经过充分测试的配置?

此外,一般来说,lld 较新,它不是大多数操作系统的默认设置,如果我们开始在更多地方使用它,几乎肯定会出现随机兼容错误。

我已经提交了两个元错误,以集中精力在两个平台上默认使用 (rust-)lld:

  • #71515 - x64 Ubuntu 20.04 LTS(以及更广泛的所有 x64 ELF 平台)
  • #71520 - x64 msvc 窗口

windows-msvc 显然状况良好,似乎对在后端使用 rust-lld 的支持有限,但我不清楚这里需要做什么。

LLD + windows-msvc处于非常好的状态,我目前正在使用此设置进行rustc开发。

lld-link所有必要支持都在rustc后端,但存在像https://github.com/rust-lang/rust/issues/68647这样的错误

  • 这可能很重要,显然 lld 会检测它是作为“ld”还是“ld.lld”执行的(需要调查)

确实如此,但 ld 和 ld.lld 是相同的模式: https :

  • 不幸的是 -fuse-ld=lld 只是 GCC 9 的一部分,所以我们可能需要功能/版本检测才能使用它(clang 已经有很长时间了)

由于 ld.lld 与 ld 相同,并且根据https://patches-gcc.linaro.org/patch/11148/,-fuse-ld=lld的唯一变化是运行 ld.lld 而不是 ld,如果我们使用PATH注入,应该没问题。 我认为将其锁定到 gcc 9+ 并不好:debian stable 只有 8.3,并且 Bullseye 可能要到 2021 年才会发布。

  • windows-mingw 似乎与 linux/unix 大致相同,除了您可能会得到一个古老的 GCC,而且事情有点不稳定,因为pseudo-windows-linux 并不是经过充分测试的配置?

mingw-w64 6.0.0,于 2018 年 9 月 16 日发布,具有 gcc 8.3.0,它没有 -fuse-ld=lld,但仍然相当新。 mingw-w64 7.0.0,于 2019 年 11 月 11 日发布,具有 gcc 9.3.0,它具有 -fuse-ld=lld。 Debian buster(稳定版)有 6.0.0,bullseye(测试版)有 7.0.0。 Debian stretch (oldstable) 只有 5.0.1 和 gcc 6.3.0,但我认为如果 gcc 6.3 存在重大问题,需要最新的 debian stable 来支持 lld 是合理的。

我已经提交了两个元错误,以集中精力在两个平台上默认使用 (rust-)lld:

  • #71515 - x64 Ubuntu 20.04 LTS(以及更广泛的所有 x64 ELF 平台)
  • #71520 - x64 msvc 窗口

关于 lld 的 macOS (Mach-O) 端口:自https://github.com/rust-lang/rust/issues/39915#issuecomment -618726211 以来,它似乎可以工作,或者至少处于明显更好的状态写了!

使用此 LLVM commit ,我构建了lld并将其设置为tokio-rs/tracing的特定于项目的链接器。 然后我针对nightly-x86_64-apple-darwin构建了跟踪并成功运行了所有测试。 我对(调试)构建时间特别满意:

  • 使用ld ,干净的cargo build需要 35 秒。
  • 使用lld ,干净的cargo build需要 20 秒。

注意:

@davidbarsky酷! 出于好奇,该性能与zld如何? ( https://github.com/michaeleisel/zld )

另外,你考虑了热量吗? MBP 会很快进入热节流状态,尤其是在默认风扇速度配置文件中。 只需等到铰链附近的机器底部摸起来凉了再进行跑步应该有助于保持一致性。

我只是这样一个错误,在 Ubuntu 16 i686 上

此页面是否有帮助?
0 / 5 - 0 等级