Skip to content

Commit ee8ae66

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

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
+++
2+
path = "2025/11/10/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, 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 include an opaque hash which depends on compiler internals and
47+
can't be easily replicated by other compilers or tools
48+
- There is no straightforward way to differentiate between Rust and C++ symbols
49+
50+
If you've ever tried to use Rust with a debugger or a profiler and found it hard
51+
to work with because you couldn't work out which functions were which, it's probably
52+
because information was being lost in the mangling scheme.
53+
54+
Rust's compiler team started working on our own mangling scheme back in 2018
55+
with [RFC 2603][rfcs#2603] (see the ["v0 Symbol Format"][v0-mangling] chapter in
56+
rustc book for our current documentation on the format). Our "v0" mangling scheme has
57+
multiple advantageous properties:
58+
59+
- An unambiguous encoding for everything that can end up in a binary's symbol table
60+
- Information about generic parameters are encoded in a reversible way
61+
- Mangled symbols are decodable such that it should be possible to identify concrete
62+
instances of generic functions
63+
- It doesn't rely on compiler internals
64+
- Symbols are restricted to only `A-Z`, `a-z`, `0-9` and `_`, helping ensure
65+
compatibility with tools on varied platforms
66+
- It tries to stay efficient and avoid unnecessarily long names and
67+
computationally-expensive decoding
68+
69+
However, rustc is not the only tool that interacts with Rust symbol names: the
70+
aforementioned debuggers, profilers and other tools all need to be updated to
71+
understand Rust's v0 symbol mangling scheme so that Rust's users can continue
72+
to work with Rust binaries using all the tools they're used to without having
73+
to look at mangled symbols. Furthermore, all of those tools need to have new
74+
releases cut and then those releases need to be picked up by distros. This takes
75+
time!
76+
77+
Fortunately, the compiler team now believe that support for our v0 mangling
78+
scheme is now sufficiently widespread that it can start to be used by default by
79+
rustc.
80+
81+
#### Benefits
82+
83+
Reading Rust backtraces, or using Rust with debuggers, profilers and other
84+
tools that operate on compiled Rust code, will be able to output much more
85+
useful and readable names. This will especially help with async code,
86+
closures and generic functions.
87+
88+
It's easy to see the new mangling scheme in action, consider the following
89+
example:
90+
91+
```rust
92+
fn foo<T>() {
93+
panic!()
94+
}
95+
96+
fn main() {
97+
foo::<Vec<(String, &[u8; 123])>>();
98+
}
99+
```
100+
101+
With the legacy mangling scheme, all of the useful information about the generic
102+
instantiation of `foo` is lost in the symbol `f::foo`..
103+
104+
```
105+
thread 'main' panicked at f.rs:2:5:
106+
explicit panic
107+
stack backtrace:
108+
0: std::panicking::begin_panic
109+
at /rustc/d6c...582/library/std/src/panicking.rs:769:5
110+
1: f::foo
111+
2: f::main
112+
3: core::ops::function::FnOnce::call_once
113+
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
114+
```
115+
116+
..but with the v0 mangling scheme, the useful details of the generic instantiation
117+
are preserved with `f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>`:
118+
119+
```
120+
thread 'main' panicked at f.rs:2:5:
121+
explicit panic
122+
stack backtrace:
123+
0: std::panicking::begin_panic
124+
at /rustc/d6c...582/library/std/src/panicking.rs:769:5
125+
1: f::foo::<alloc::vec::Vec<(alloc::string::String, &[u8; 123])>>
126+
2: f::main
127+
3: <fn() as core::ops::function::FnOnce<()>>::call_once
128+
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
129+
```
130+
131+
#### Possible drawbacks
132+
133+
Symbols using the v0 mangling scheme can be larger than symbols with the
134+
legacy mangling scheme, which can result in a slight increase in linking
135+
times and binary sizes if symbols aren't stripped (which they aren't by default).
136+
Fortunately this impact should be minor, especially with modern linkers like
137+
lld, which Rust [will now default to on some targets][switch-to-lld].
138+
139+
Some old versions of tools/distros or niche tools that the compiler team are
140+
unaware of may not have had support for the v0 mangling scheme added. When
141+
using these tools, the only consequence is that users may encounter mangled
142+
symbols. [rustfilt] can be used to demangle Rust symbols if a tool does not.
143+
144+
In any case, using the new mangling scheme can be disabled if any problem
145+
occurs: use the `-Csymbol-mangling-version=legacy -Zunstable-options` flag
146+
to revert to using the legacy mangling scheme.
147+
148+
Explicitly enabling the legacy mangling scheme requires nightly, it is not
149+
intended to be stabilised so that support can eventually be removed.
150+
151+
#### Adding v0 support in your tools
152+
153+
If you maintain a tool that interacts with Rust symbols and does not
154+
support the v0 mangling scheme, there are Rust and C implementations
155+
of a v0 symbol demangler available in the [rust-lang/rustc-demangle]
156+
repository that can be integrated into your project.
157+
158+
#### Summary
159+
160+
rustc will use our "v0" mangling scheme on nightly for all targets
161+
starting in tomorrow's rustup nightly (`nightly-2025-11-11`).
162+
163+
Let us know if you encounter problems, by [opening an
164+
issue](https://github.com/rust-lang/rust/issues/new/choose) on GitHub.
165+
166+
If that happens, you can use the legacy mangling scheme with
167+
the `-Csymbol-mangling-version=legacy -Zunstable-options` flag.
168+
Either by adding it to the usual `RUSTFLAGS` environment variable, or to a
169+
project's [`.cargo/config.toml`] configuration file, like so:
170+
171+
```toml
172+
[build]
173+
rustflags = ["-Csymbol-mangling-version=legacy", "-Zunstable-options"]
174+
```
175+
176+
If you like the sound of the new symbol mangling version and would
177+
like to start using it on stable or beta channels of Rust, then you can
178+
similarly use the `-Csymbol-mangling-version=v0` flag today via
179+
`RUSTFLAGS` or [`.cargo/config.toml`]:
180+
181+
```toml
182+
[build]
183+
rustflags = ["-Csymbol-mangling-version=v0"]
184+
```
185+
186+
[`.cargo/config.toml`]: (https://doc.rust-lang.org/cargo/reference/config.html)
187+
[rfcs#2603]: https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html
188+
[itanium-mangling]: https://refspecs.linuxbase.org/cxxabi-1.86.html#mangling
189+
[v0-mangling]: https://doc.rust-lang.org/nightly/rustc/symbol-mangling/v0.html
190+
[switch-to-lld]: https://blog.rust-lang.org/2025/09/01/rust-lld-on-1.90.0-stable/
191+
[rustfilt]: https://github.com/luser/rustfilt
192+
[rust-lang/rustc-demangle]: https://github.com/rust-lang/rustc-demangle

0 commit comments

Comments
 (0)