Skip to content

Commit 0836ea0

Browse files
committed
blog post for v0 mangling on nightly
1 parent 423d051 commit 0836ea0

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
+++
2+
path = "2025/10/28/switching-to-v0-mangling-on-nightly"
3+
title = "Switching to Rust's own mangling scheme on nightly"
4+
authors = ["David Wood"]
5+
6+
[extra]
7+
team = "the compiler team"
8+
team_url = "https://www.rust-lang.org/governance/teams/compiler"
9+
+++
10+
11+
**TL;DR:** rustc will use its own "v0" mangling scheme by default on nightly
12+
versions instead of the previous default scheme which re-used C++'s mangling
13+
scheme
14+
15+
#### Context
16+
17+
When Rust is compiled into object files and binaries, each item (functions,
18+
statics, etc) must have a globally unique "symbol" identifying it.
19+
20+
In C, the symbol name of a function is just the name that the function was
21+
defined with, such as `strcmp`. This is straightforward and easy to
22+
understand, but requires that each item have a globally unique name
23+
that doesn't overlap with any symbols from libraries that it is linked
24+
against. If two items had the same symbol then when the linker tried to resolve
25+
a symbol to an address in memory (of a function, say), then it wouldn't know
26+
which symbol is the correct one.
27+
28+
Languages like Rust and C++ define "symbol mangling schemes", leveraging information
29+
from the type system to give each item a unique symbol name. Without this, it would be
30+
possible to produce clashing symbols in a variety of ways - for example, every
31+
instantiation of a generic or templated function (or an overload in C++), which all
32+
have the same name in the surface language would end up with clashing symbols; or
33+
the same name in different modules, such as `a::foo` and `b::foo` would have clashing
34+
symbols.
35+
36+
Rust originally used a symbol mangling scheme based on the
37+
[Itanium ABI's name mangling scheme][itanium-mangling] used by C++ (sometimes). Over
38+
the years, it was extended in an inconsistent and ad-hoc way to support Rust
39+
features that the mangling scheme wasn't originally designed for. Rust's current legacy
40+
mangling scheme has a number of drawbacks:
41+
42+
- Information about generic parameter instantiations is lost during mangling
43+
- It is internally inconsistent - some paths use an Itanium ABI-style encoding
44+
but some don't
45+
- Symbol names can contain `.` characters which aren't supported on all platforms
46+
- Symbol names depend on compiler internals and can't be easily replicated by
47+
other compilers or tools
48+
49+
If you've ever tried to use Rust with a debugger or a profiler and found it hard
50+
to work with because you couldn't work out which functions were which, it's probably
51+
because information was being lost in the mangling scheme.
52+
53+
Rust's compiler team started working on our own mangling scheme back in 2018
54+
with [RFC 2603][rfcs#2603] (see the ["v0 Symbol Format"][v0-mangling] chapter in
55+
rustc book for our current documentation on the format). Our "v0" mangling scheme has
56+
multiple advantageous properties:
57+
58+
- An unambiguous encoding for everything that can end up in a binary's symbol table
59+
- Information about generic parameters are encoded in a reversible way
60+
- Mangled symbols are decodable such that it should be possible to identify concrete
61+
instances of generic functions
62+
- It doesn't rely on compiler internals
63+
- Symbols are restricted to only `A-Z`, `a-z`, `0-9` and `_`, helping ensure
64+
compatibility with tools on varied platforms
65+
- It tries to stay efficient and avoid unnecessarily long names and
66+
computationally-expensive decoding
67+
68+
However, rustc is not the only tool that interacts with Rust symbol names: the
69+
aforementioned debuggers, profilers and other tools all need to be updated to
70+
understand Rust's v0 symbol mangling scheme so that Rust's users can continue
71+
to work with Rust binaries using all the tools they're used to without having
72+
to look at mangled symbols. Furthermore, all of those tools need to have new
73+
releases cut and then those releases need to be picked up by distros. This takes
74+
time!
75+
76+
Fortunately, the compiler team now believe that support for our v0 mangling
77+
scheme is now sufficiently widespread that it can start to be used by default by
78+
rustc.
79+
80+
#### Benefits
81+
82+
Reading Rust backtraces, or using Rust with debuggers, profilers and other
83+
tools that operate on compiled Rust code, will be able to output much more
84+
useful and readable names. This will especially help with async code,
85+
closures and generic functions.
86+
87+
It's easy to see the new mangling scheme in action, consider the following
88+
example:
89+
90+
```rust
91+
fn foo<T>() {
92+
panic!()
93+
}
94+
95+
fn main() {
96+
foo::<Vec<(String, &[u8; 123])>>();
97+
}
98+
```
99+
100+
With the legacy mangling scheme, all of the useful information about the generic
101+
instantiation of `foo` is lost in the symbol `f::foo`..
102+
103+
```
104+
thread 'main' panicked at f.rs:2:5:
105+
explicit panic
106+
stack backtrace:
107+
0: std::panicking::begin_panic
108+
at /rustc/d6c...582/library/std/src/panicking.rs:769:5
109+
1: f::foo
110+
2: f::main
111+
3: core::ops::function::FnOnce::call_once
112+
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
113+
```
114+
115+
..but with the v0 mangling scheme, the useful details of the generic instantiation
116+
are preserved with `f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>`:
117+
118+
```
119+
thread 'main' panicked at f.rs:2:5:
120+
explicit panic
121+
stack backtrace:
122+
0: std::panicking::begin_panic
123+
at /rustc/d6c...582/library/std/src/panicking.rs:769:5
124+
1: f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>
125+
2: f::main
126+
3: <fn() as core::ops::function::FnOnce<()>>::call_once
127+
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
128+
```
129+
130+
#### Possible drawbacks
131+
132+
Symbols using the v0 mangling scheme can be larger than symbols with the
133+
legacy mangling scheme, which can result in a slight increase in linking
134+
times and binary sizes if symbols aren't stripped (which they aren't by default).
135+
Fortunately this impact should be minor, especially with modern linkers like
136+
lld, which Rust [will now default to on some targets][switch-to-lld].
137+
138+
Some old versions of tools/distros or niche tools that the compiler team are
139+
unaware of may not have had support for the v0 mangling scheme added. When
140+
using these tools, the only consequence is that users may encounter mangled
141+
symbols. [rustfilt] can be used to demangle Rust symbols if a tool does not.
142+
143+
In any case, using the new mangling scheme can be disabled if any problem
144+
occurs: use the `-Csymbol-mangling-version=legacy -Zunstable-options` flag
145+
to revert to using the legacy mangling scheme.
146+
147+
#### Summary
148+
149+
rustc will use our "v0" mangling scheme on nightly for all targets
150+
starting in tomorrow's rustup nightly (`nightly-2025-XX-XX`).
151+
152+
Let us know if you encounter problems, by [opening an
153+
issue](https://github.com/rust-lang/rust/issues/new/choose) on GitHub.
154+
155+
If that happens, you can use the legacy mangling scheme with
156+
the `-Csymbol-mangling-version=legacy -Zunstable-options` flag.
157+
Using the legacy mangling scheme requires nightly, it is not intended
158+
to be stabilised so that support can eventually be removed. Either by
159+
adding it to the usual `RUSTFLAGS` environment variable, or to a
160+
project's [`.cargo/config.toml`] configuration file, like so:
161+
162+
```toml
163+
[build]
164+
rustflags = ["-Csymbol-mangling-version=legacy", "-Zunstable-options"]
165+
```
166+
167+
If you like the sound of the new symbol mangling version and would
168+
like to start using it on stable or beta channels of Rust, then you can
169+
similarly use the `-Csymbol-mangling-version=v0` flag today via
170+
`RUSTFLAGS` or [`.cargo/config.toml`]:
171+
172+
```toml
173+
[build]
174+
rustflags = ["-Csymbol-mangling-version=v0"]
175+
```
176+
177+
[`.cargo/config.toml`]: (https://doc.rust-lang.org/cargo/reference/config.html)
178+
[rfcs#2603]: https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html
179+
[itanium-mangling]: https://refspecs.linuxbase.org/cxxabi-1.86.html#mangling
180+
[v0-mangling]: https://doc.rust-lang.org/nightly/rustc/symbol-mangling/v0.html
181+
[switch-to-lld]: https://blog.rust-lang.org/2025/09/01/rust-lld-on-1.90.0-stable/
182+
[rustfilt]: https://github.com/luser/rustfilt

0 commit comments

Comments
 (0)