切换至Rust自研的符号重整方案(Nightly版本)
发布日期:2025年11月20日 作者:David Wood 代表编译器团队
摘要
从 nightly-2025-11-21 开始,rustc(Rust编译器)将在nightly版本中默认使用其自研的 “v0”符号重整方案,而不再沿用先前默认的、复用C++符号重整的方案。
背景
当Rust代码被编译为目标文件和二进制文件时,每个项(如函数、静态变量等)都需要一个全局唯一的 “符号” 来标识。
在C语言中,函数的符号名就是其定义时使用的名称(例如 strcmp)。这种方法简单易懂,但要求每个项都有一个全局唯一的名称,且不能与链接库中的任何符号冲突。如果两个项拥有相同的符号,链接器在尝试将符号解析到内存地址(例如函数地址)时,将无法确定哪个是正确的。
像Rust和C++这样的语言定义了 “符号重整方案” ,利用类型系统的信息为每个项生成唯一的符号名称。如果没有这种机制,就可能以多种方式产生冲突的符号。例如:泛型或模板函数(或C++中的重载函数)的每个实例在表层语言中具有相同的名称,最终却会产生冲突的符号;或者不同模块中的相同名称(如 a::foo 和 b::foo)也会产生符号冲突。
Rust最初使用的符号重整方案是基于 C++ Itanium ABI的名称重整方案。多年来,为了支持该方案最初并未设计的Rust特性,它以一种不一致的、临时性的方式进行了扩展。Rust当前的遗留重整方案存在诸多缺点:
- 有关泛型参数实例化的信息在重整过程中丢失。
- 内部不一致——有些路径使用了Itanium ABI风格的编码,而有些则没有。
- 符号名可能包含
.字符,这在某些平台上不被支持。 - 符号名包含一个不透明的哈希值,该值依赖于编译器内部实现,其他编译器或工具难以复现。
- 没有简单的方法来区分Rust符号和C++符号。
如果你曾尝试在调试器或性能分析器中使用Rust,却因无法分辨不同函数而感到困难,那很可能是因为重整方案导致了信息丢失。
Rust编译器团队早在2018年就开始研发我们自己的重整方案(参见RFC 2603,当前格式的文档可查阅rustc手册中的“v0符号格式”章节)。我们的 “v0”重整方案 具有以下优势:
- 为所有可能出现在二进制文件符号表中的内容提供了明确的编码。
- 以可逆的方式编码泛型参数信息。
- 重整后的符号是可解码的,从而可以识别泛型函数的具体实例。
- 不依赖于编译器内部实现。
- 符号仅限于
A-Z、a-z、0-9和_,有助于确保与不同平台上工具的兼容性。 - 力求高效,避免不必要地生成过长的名称和进行计算开销大的解码。
然而,rustc 并非唯一与Rust符号名交互的工具:前文提到的调试器、性能分析器以及其他工具都需要更新,以理解Rust的v0符号重整方案。这样,Rust用户才能继续使用他们习惯的所有工具来处理Rust二进制文件,而无需查看重整后的符号。此外,所有这些工具都需要发布新版本,然后由各大发行版采纳。这需要时间!
幸运的是,编译器团队现在认为,对我们v0重整方案的支持已经足够广泛,可以开始让 rustc 默认使用它了。
优势
读取Rust的回溯信息,或者在使用调试器、性能分析器等处理已编译Rust代码的工具时,将能够输出更有用、更易读的名称。这对于异步代码、闭包和泛型函数尤其有帮助。
可以通过以下示例轻松看到新重整方案的效果:
|
|
使用遗留重整方案时,有关 foo 泛型实例化的所有有用信息都在符号 f::foo.. 中丢失了:
|
|
但使用v0重整方案后,泛型实例化的有用细节得以保留,符号为 f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>:
|
|
可能的缺点
使用v0重整方案的符号可能比使用遗留方案的符号更长,如果符号未被剥离(默认情况下不会剥离),可能会导致链接时间和二进制文件大小略有增加。幸运的是,这种影响应该是微小的,特别是在使用现代链接器(如 lld)时,Rust现在会在某些目标上默认使用它。
一些旧版本的工具/发行版或编译器团队未知的利基工具可能尚未添加对v0重整方案的支持。使用这些工具时,唯一的后果是用户可能会遇到重整后的符号。如果某个工具不支持,可以使用 rustfilt 来反重整Rust符号。
无论如何,如果出现任何问题,可以禁用新的重整方案:使用 -Csymbol-mangling-version=legacy -Zunstable-options 标志来恢复使用遗留重整方案。显式启用遗留重整方案需要nightly版本,并且不打算将其稳定化,以便最终能够移除对其的支持。
在您的工具中添加v0支持
如果您维护一个与Rust符号交互但不支持v0重整方案的工具,可以在 rust-lang/rustc-demangle 代码仓库中找到Rust和C语言实现的v0符号反重整器,可以将其集成到您的项目中。
总结
从明天(2025年11月21日)的rustup nightly版本(nightly-2025-11-21)开始,rustc 将在所有目标平台上默认使用我们的 “v0”重整方案。
|
|
如果您喜欢新的符号重整版本,并希望开始在Rust的stable或beta频道上使用它,那么您今天就可以通过 RUSTFLAGS 或 .cargo/config.toml 类似地使用 -Csymbol-mangling-version=v0 标志:
|
|