v0 符号格式
v0 修饰格式在 RFC 2603 中引入。它具有以下特性
- 它为最终出现在二进制文件符号表中的所有内容提供了一种明确的字符串编码。
- 它以可逆的方式对泛型参数信息进行编码。
- 修饰后的符号是可解码的,因此解码后的形式应该很容易识别为例如多态函数的某个具体实例。
- 它有一个一致的定义,不依赖于某些语言结构的漂亮打印。
- 符号可以被限制为仅包含字符
A-Z
、a-z
、0-9
和_
。这有助于确保它与平台无关,在某些上下文中其他字符可能具有特殊含义(例如,MSVCDEF
文件中的.
)。可选地支持 Unicode 符号。 - 它试图保持效率,避免不必要的长名称,并避免解码时进行计算量大的操作。
v0 格式不打算与其他修饰方案(如 C++)兼容。
v0 格式不被视为 Rust 的稳定 ABI。目前,这种格式的目的是定义得足够好,以便解码器可以生成符号的合理的人类可读形式。有一些实现定义的部分导致无法完全预测给定的 Rust 实体将如何编码。
以下部分定义了 v0 符号的编码。符号没有标准化的解码形式,但提供了有关如何解码符号的建议。实现者可以选择以不同的方式进行解码。
扩展
将来可能会扩展此格式,以在 Rust 扩展了新的语言项时添加新的标签。为了保持向前兼容性,解码器应该能够优雅地处理在编码中遇到本文件中未描述的标签字符的符号。例如,它们可以回退到显示修饰后的符号。可以在任何包含标签字符的地方扩展格式,例如 类型 规则。现有标签和编码的含义不会改变。
语法表示法
编码符号的格式以扩展的类似 BNF 的语法形式的上下文无关语法来表示。可以在 符号语法摘要 中找到一个综合的摘要。
名称 | 语法 | 示例 | 描述 |
---|---|---|---|
规则 | → | 一个产生式。 | |
串联 | 空格 | 从左到右按顺序排列的各个元素。 | |
备选 | | | 匹配其中一个或另一个。 | |
分组 | () | 将多个元素作为一个元素进行分组。 | |
重复 | {} | 重复封闭元素零次或多次。 | |
选项 | opt | 一个可选元素。 | |
字面量 | 等宽字体 | G | 一个匹配确切字符(区分大小写)的终结符。 |
符号名称
symbol-name →
_R
decimal-numberopt path instantiating-crateopt vendor-specific-suffixopt
修饰后的符号以两个字符_R
开头,这是一个前缀,用于标识符号为 Rust 符号。前缀后可以可选地跟着一个decimal-number,它指定编码版本。此数字目前未使用,并且在当前编码中从未出现。接下来是一个path,它对实体的路径进行编码。路径后跟一个可选的instantiating-crate,它有助于消除可能在不同板条箱中多次实例化的实体的歧义。最后部分是一个可选的vendor-specific-suffix。
推荐的解码
symbol-name 应该显示为path。instantiating-crate 和vendor-specific-suffix 通常不需要显示。
示例
std::path::PathBuf::new();
板条箱
mycrate
中的PathBuf::new
符号是_RNvMsr_NtCs3ssYzQotkvD_3std4pathNtB5_7PathBuf3newCs15kBYyAo9fc_7mycrate ├┘└───────────────────────┬──────────────────────┘└──────────┬─────────┘ │ │ │ │ │ └── instantiating-crate path "mycrate" │ └───────────────────────────────────── path to std::path::PathBuf::new └─────────────────────────────────────────────────────────────── `_R` symbol prefix
推荐的解码:
<std::path::PathBuf>::new
符号路径
path →
crate-root
| inherent-impl
| trait-impl
| trait-definition
| nested-path
| generic-args
| backref
路径表示对某个实体的Rust 路径的变体。除了使用标识符的典型 Rust 路径段之外,它还使用额外的元素来表示不可命名的实体(如 impl
)或单态化项的泛型参数。
初始标签字符可用于确定它表示哪种路径。
路径:箱根
箱根 →
C
标识符
箱根表示指向箱模块树根的路径。它由字符 C
后跟箱名称作为 标识符 组成。
箱名称是从定义箱中看到的名称。由于 Rust 支持将多个具有相同名称的箱链接起来,因此使用 消歧符 来使名称在整个箱图中唯一。
推荐的解码
箱根可以显示为标识符,例如
mycrate
。通常,标识符中的消歧符不需要显示,但作为备用形式,消歧符可以以十六进制显示,例如
mycrate[ca63f166dbe9294]
。
示例
fn example() {}
箱
mycrate
中example
的符号是_RNvCs15kBYyAo9fc_7mycrate7example │└────┬─────┘││└──┬──┘ │ │ ││ │ │ │ ││ └── crate-root identifier "mycrate" │ │ │└────── length 7 of "mycrate" │ │ └─────── end of base-62-number │ └────────────── disambiguator for crate-root "mycrate" 0xca63f166dbe9293 + 1 └──────────────────── crate-root
推荐反混淆:
mycrate::example
路径:内联实现
内联实现表示指向内联实现的路径。它由字符 M
后跟 impl-path 组成,它唯一标识了定义项的 impl 块。之后是一个 类型,表示 impl 的 Self
类型。
推荐的解码
示例
struct Example; impl Example { fn foo() {} }
Example
的 impl 中foo
的符号是_RNvMs_Cs4Cv8Wi1oAIB_7mycrateNtB4_7Example3foo │├┘└─────────┬──────────┘└────┬──────┘ ││ │ │ ││ │ └── Self type "Example" ││ └─────────────────── path to the impl's parent "mycrate" │└─────────────────────────────── disambiguator 1 └──────────────────────────────── inherent-impl
推荐反混淆:
<mycrate::Example>::foo
路径:特征实现
特征实现表示指向特征实现的路径。它由字符 X
后跟 impl-path 到 impl 的父级,然后是表示 impl 的 Self
类型的 类型,最后是 路径 到特征。
推荐的解码
特征实现可以显示为使用
<
类型as
路径>
语法的限定路径段。impl-path 通常不需要显示。
示例
struct Example; trait Trait { fn foo(); } impl Trait for Example { fn foo() {} }
Example
的特征实现中foo
的符号是_RNvXCs15kBYyAo9fc_7mycrateNtB2_7ExampleNtB2_5Trait3foo │└─────────┬──────────┘└─────┬─────┘└────┬────┘ │ │ │ │ │ │ │ └── path to the trait "Trait" │ │ └────────────── Self type "Example" │ └──────────────────────────────── path to the impl's parent "mycrate" └─────────────────────────────────────────── trait-impl
推荐反混淆:
<mycrate::Example as mycrate::Trait>::foo
路径:实现
impl-path 是用于 内联实现 和 特征实现 的路径,用于指示指向实现父级的路径。它由一个可选的 消歧符 后跟一个 路径 组成。路径 是指向包含 impl 的父级的路径。消歧符 可用于区分同一父级内的多个 impl。
推荐的解码
impl-path 通常不需要显示(除非需要 impl 的位置)。
示例
struct Example; impl Example { fn foo() {} } impl Example { fn bar() {} }
Example
的 impl 中foo
的符号是_RNvMCs7qp2U7fqm6G_7mycrateNtB2_7Example3foo └─────────┬──────────┘ │ └── path to the impl's parent crate-root "mycrate"
bar
的符号类似,但它有一个消歧符来指示它位于不同的 impl 块中。_RNvMs_Cs7qp2U7fqm6G_7mycrateNtB4_7Example3bar ├┘└─────────┬──────────┘ │ │ │ └── path to the impl's parent crate-root "mycrate" └────────────── disambiguator 1
推荐反混淆
foo
:<mycrate::Example>::foo
bar
:<mycrate::Example>::bar
路径:特征定义
特征定义是指向特征定义的路径。它由字符 Y
后跟 类型 组成,它是引用者的 Self
类型,然后是 路径 到特征定义。
推荐的解码
特征定义可以显示为使用
<
类型as
路径> 语法的限定路径段。
示例
trait Trait { fn example() {} } struct Example; impl Trait for Example {}
为
Example
实现的特征Trait
中example
的符号是_RNvYNtCs15kBYyAo9fc_7mycrate7ExampleNtB4_5Trait7exampleB4_ │└──────────────┬───────────────┘└────┬────┘ │ │ │ │ │ └── path to the trait "Trait" │ └──────────────────────── path to the implementing type "mycrate::Example" └──────────────────────────────────────── trait-definition
推荐反混淆:
<mycrate::Example as mycrate::Trait>::example
路径:嵌套路径
嵌套路径是表示可选命名实体的路径。它由字符 N
后跟 命名空间 组成,表示实体的命名空间,然后是 路径,它表示实体的父级,最后是实体的 标识符。
当实体未命名时,实体的标识符的长度可能为 0。例如,闭包、元组式结构体构造函数和匿名常量可能没有名称。除非消歧符为 0,否则标识符可能仍然具有消歧符。
推荐的解码
嵌套路径可以通过首先显示 路径,然后显示
::
分隔符,最后显示 标识符 来显示。如果 标识符 为空,则不应显示分隔符::
。如果指定了 命名空间,则可能会添加额外的上下文,例如
路径::{
命名空间 (:
标识符)opt#
消歧符作为十进制数}
这里,命名空间
C
可以打印为closure
,S
可以打印为shim
。其他命名空间可以通过其字符标签打印。如果名称为空,则可以跳过:
名称 部分。如果指定了 命名空间,则可能会显示 标识符 中的 消歧符。在其他情况下,通常不需要显示 消歧符。如果显示,建议将其放在方括号中,例如
[284a76a8b41a7fd3]
。如果 消歧符 不存在,则其值为 0,并且始终可以从显示中省略。
示例
fn main() { let x = || {}; let y = || {}; x(); y(); }
箱
mycrate
中闭包x
的符号是_RNCNvCsgStHSCytQ6I_7mycrate4main0B3_ ││└─────────────┬─────────────┘│ ││ │ │ ││ │ └── identifier with length 0 ││ └───────────────── path to "mycrate::main" │└──────────────────────────────── closure namespace └───────────────────────────────── nested-path
闭包
y
的符号类似,但带有一个消歧符_RNCNvCsgStHSCytQ6I_7mycrate4mains_0B3_ ││ │└── base-62-number 0 └─── disambiguator 1 (base-62-number+1)
推荐反混淆
x
:mycrate::main::{closure#0}
y
:mycrate::main::{closure#1}
路径:泛型参数
泛型参数是表示泛型参数列表的路径。它由字符 I
后跟 路径 到定义实体,然后是零个或多个 泛型参数,最后是字符 E
。
每个 泛型参数 都是 生命周期(以字符 L
开头)、类型 或字符 K
后跟 常量,表示常量参数。
推荐的解码
泛型参数可以打印为:路径
::
opt<
用逗号分隔的参数列表>
。对于类型路径,::
分隔符可以省略(类似于 Rust 的规则)。
示例
fn main() { example([123]); } fn example<T, const N: usize>(x: [T; N]) {}
函数
example
的符号是_RINvCsgStHSCytQ6I_7mycrate7examplelKj1_EB2_ │└──────────────┬───────────────┘││││││ │ │ │││││└── end of generic-args │ │ ││││└─── end of const-data │ │ │││└──── const value `1` │ │ ││└───── const type `usize` │ │ │└────── const generic │ │ └─────── generic type i32 │ └──────────────────────── path to "mycrate::example" └──────────────────────────────────────── generic-args
推荐反混淆:
mycrate::example::<i32, 1>
命名空间
命名空间用于将名称隔离到单独的逻辑组中,从而使相同的名称能够避免冲突。它由一个大写或小写 ASCII 字母组成。小写字母保留用于实现内部消歧类别(反混淆器永远不应该显示它们)。大写字母用于特殊命名空间,反混淆器可能会以特殊方式显示它们。
大写命名空间是
C
— 闭包。S
— shim。在某些情况下,编译器会添加 shim,这些情况下需要中间体。例如,指向具有#[track_caller]
属性的函数的fn()
指针需要一个 shim 来处理隐式调用者位置。
推荐的解码
有关推荐的反混淆,请参见 嵌套路径。
标识符
字节 → {UTF-8 字节}
标识符是 路径 中用于引用实体的命名标签。它由一个可选的 消歧符 后跟一个 未消歧标识符 组成。
消歧符用于消歧不应该被视为相同的相同标识符。例如,闭包没有名称,因此消歧符是同一父路径中两个不同闭包之间唯一的区分元素。
未消歧标识符以一个可选的 u
字符开头,表示标识符以Punycode 编码。下一部分是 十进制数,表示 字节 的长度。
在标识符大小之后是一个可选的 _
字符,用于将长度值与标识符本身隔开。如果 字节 以十进制数字或 _
开头,则 _
是必需的,以便在 十进制数 结束和 字节 开始的地方保持明确。
字节 是以 UTF-8 编码的标识符本身。
推荐的解码
标识符 的显示可能取决于其上下文。如果它是 Punycode 编码的,则它可能首先被解码,然后才显示。
消歧符 可能会或可能不会显示;请参阅使用 标识符 的规则的建议。
Punycode 标识符
由于某些环境仅限于 ASCII 字母数字和_
,Rust 的 Unicode 标识符 可以使用修改后的 Punycode 版本进行编码。
例如,函数
mod gödel {
mod escher {
fn bach() {}
}
}
将被混淆为
_RNvNtNtCsgOH4LzxkuMq_7mycrateu8gdel_5qa6escher4bach
││└───┬──┘
││ │
││ └── gdel_5qa translates to gödel
│└─────── 8 is the length
└──────── `u` indicates it is a Unicode identifier
标准 Punycode 生成形式为([[:ascii:]]+-)?[[:alnum:]]+
的字符串。这存在问题,因为-
字符(用于将 ASCII 部分与 36 进制编码分隔)不在符号支持的字符集中。因此,Punycode 编码中的-
字符将被替换为_
。
以下是一些示例
原始 | Punycode | Punycode + 编码 |
---|---|---|
føø | f-5gaa | f_5gaa |
α_ω | _-ylb7e | __ylb7e |
铁锈 | n84amf | n84amf |
🤦 | fq9h | fq9h |
ρυστ | 2xaedc | 2xaedc |
注意:是否使用 Punycode 对标识符进行编码由编译器决定。某些平台可能原生支持 UTF-8 符号,编译器可能会选择直接使用 UTF-8 编码。反混淆器应准备好支持两种形式。
消歧符
disambiguator →
s
base-62-number
消歧符 用于符号 路径 的各个部分,以唯一标识原本相同但应视为不同的路径元素。它以字符s
开头,后面跟着一个 base-62-number。
如果未指定 消歧符,则其值可以假定为零。否则,在反混淆时,应将值 1 添加到 base-62-number(因此编码为_
的 base-62-number 为零的值为 1)。这允许按顺序编码的消歧符使用最少的字节。
推荐的解码
消歧符 可能会或可能不会显示;请参阅使用 消歧符 的规则的建议。
生命周期
lifetime →
L
base-62-number
生命周期 用于编码匿名(编号)生命周期,无论是被擦除的还是 更高阶的。它以字符L
开头,后面跟着一个 base-62-number。索引 0 始终被擦除。从 1 开始的索引(作为 De Bruijn 索引)引用由一个或多个封闭 绑定器 绑定的更高阶生命周期边界。
推荐的解码
生命周期 可以像 Rust 生命周期一样使用单引号显示。
索引 0 应显示为
'_
。对于 ref-type、mut-ref-type 或 dyn-trait-type 中的生命周期,索引 0 不应显示。生命周期可以通过将 De Bruijn 索引转换为 De Bruijn 层级(层级 = 绑定生命周期的数量 - 索引)并为每个层级选择一个唯一的名称来显示。例如,从单个小写字母开始,如层级 0 的
'a
。层级超过 25 时,可以考虑打印数字生命周期,如'_123
。有关生命周期索引和排序的更多信息,请参阅 绑定器。
示例
fn main() { example::<fn(&u8, &u16)>(); } pub fn example<T>() {}
函数
example
的符号是_RINvCs7qp2U7fqm6G_7mycrate7exampleFG0_RL1_hRL0_tEuEB2_ │└┬┘│└┬┘││└┬┘││ │ │ │ │ ││ │ │└── end of input types │ │ │ │ ││ │ └─── type u16 │ │ │ │ ││ └───── lifetime #1 'b │ │ │ │ │└─────── reference type │ │ │ │ └──────── type u8 │ │ │ └────────── lifetime #2 'a │ │ └──────────── reference type │ └────────────── binder with 2 lifetimes └──────────────── function type
推荐的反混淆:
mycrate::example::<for<'a, 'b> fn(&'a u8, &'b u16)>
常量
const-data →
n
opt {十六进制数字}_
常量 用于编码泛型和类型中使用的常量值。它具有以下形式
常量数据 的编码取决于类型
bool
— 值false
编码为0_
,值 true 编码为1_
。char
— 字符的 Unicode 标量值以十六进制编码。- 无符号整数 — 值以十六进制编码。
- 有符号整数 — 字符
n
是一个前缀,表示它是负数,后面跟着以十六进制编码的绝对值。
推荐的解码
常量 可以根据类型显示为常量值。
p
占位符应显示为_
字符。对于特定类型
b
(bool) — 显示为true
或false
。c
(char) — 将字符显示为 Rust 字符(例如'A'
或'\n'
)。- 整数 — 显示整数(以十进制或十六进制形式)。
示例
fn main() { example::<0x12345678>(); } pub fn example<const N: u64>() {}
函数
example
的符号为_RINvCs7qp2U7fqm6G_7mycrate7exampleKy12345678_EB2_ ││└───┬───┘ ││ │ ││ └── const-data 0x12345678 │└─────── const type u64 └──────── const generic arg
推荐的反混淆:
mycrate::example::<305419896>
占位符
占位符 可能会出现在类型或常量值不相关的情况下。
示例
pub struct Example<T, const N: usize>([T; N]); impl<T, const N: usize> Example<T, N> { pub fn foo() -> &'static () { static EXAMPLE_STATIC: () = (); &EXAMPLE_STATIC } }
在此示例中,静态
EXAMPLE_STATIC
不会被类型或常量参数T
和N
单态化。这些将使用占位符来表示这些泛型参数。它的符号为_RNvNvMCsd9PVOYlP1UU_7mycrateINtB4_7ExamplepKpE3foo14EXAMPLE_STATIC │ │││ │ ││└── const placeholder │ │└─── const generic argument │ └──── type placeholder └────────────────── generic-args
推荐的反混淆:
<mycrate::Example<_, _>>::foo::EXAMPLE_STATIC
类型
type →
基本类型
| 数组类型
| 切片类型
| 元组类型
| 引用类型
| 可变引用类型
| 常量指针类型
| 可变指针类型
| 函数类型
| 动态特征类型
| 路径
| backref
类型 表示 Rust 类型。初始字符可用于区分编码的类型。基于初始标记字符的类型编码如下
- 基本类型 编码为单个字符
a
—i8
b
—bool
c
—char
d
—f64
e
—str
f
—f32
h
—u8
i
—isize
j
—usize
l
—i32
m
—u32
n
—i128
o
—u128
s
—i16
t
—u16
u
— 单位()
v
— 可变参数...
x
—i64
y
—u64
z
—!
p
— 占位符_
其余的基元编码为箱子生成,例如C4f128
。
-
A
— 数组[T; N]
。 -
S
— 切片[T]
。切片类型 →
S
类型标记
S
后面跟着切片的 类型。 -
T
— 元组(T1, T2, T3, ...)
。元组类型 →
T
{类型}E
标记
T
后面跟着一个或多个 类型,表示每个字段的类型,后面跟着一个终止符E
字符。注意,零长度元组(单位)使用
u
基本类型 编码。 -
R
— 引用&T
。 -
Q
— 可变引用&mut T
。 -
P
— 常量原始指针*const T
。标记
P
后面跟着指针的 类型。常量指针类型 →
P
类型 -
O
— 可变原始指针*mut T
。可变指针类型 →
O
类型标记
O
后面跟着指针的 类型。 -
F
— 函数指针fn(…) -> …
。函数类型 →
F
函数签名函数签名 → 绑定器opt
U
opt (K
ABI)opt {类型}E
类型ABI →
C
| 未消歧标识符标记
F
后面跟着函数签名的 函数签名。函数签名 是函数指针的签名。它以一个可选的 绑定器 开头,表示更高阶的特征边界 (
for<…>
)。后面跟着一个可选的
U
字符,它存在于unsafe
函数中。后面跟着一个可选的
K
字符,它表示指定了 ABI。如果未指定 ABI,则假定为"Rust"
ABI。ABI 可以是字母
C
,表示它是"C"
ABI。否则,它是一个 未消歧标识符,表示 ABI 字符串,其中连字符已转换为下划线。后面跟着零个或多个 类型,表示函数的输入参数。
后面跟着字符
E
,然后是返回值的 类型。
-
D
— 特征对象dyn Trait<Assoc=X> + Send + 'a
。dyn-trait-type →
D
dyn-bounds lifetimedyn-bounds → binderopt {dyn-trait}
E
dyn-trait → path {dyn-trait-assoc-binding}
dyn-trait-assoc-binding →
p
undisambiguated-identifier type标签
D
后面跟着一个 dyn-bounds,它编码了特征边界,后面跟着一个 lifetime,表示特征对象生命周期边界。一个 dyn-bounds 以一个可选的 binder 开头,它表示高阶特征边界 (
for<…>
)。后面跟着一个 dyn-trait 序列,以字符E
结束。每个 dyn-trait 代表一个特征边界,它包含一个指向特征的 path,后面跟着零个或多个 dyn-trait-assoc-binding,列出了关联类型。
每个 dyn-trait-assoc-binding 包含一个字符
p
,后面跟着一个 undisambiguated-identifier,表示关联绑定名称,最后是一个 type。
推荐的解码
一个 type 可以显示为它所代表的类型,使用典型的 Rust 语法来表示该类型。
示例
fn main() { example::<[u16; 8]>(); } pub fn example<T>() {}
函数
example
的符号为_RINvCs7qp2U7fqm6G_7mycrate7exampleAtj8_EB2_ │││├┘│ ││││ └─── end of generic args │││└───── const data 8 ││└────── const type usize │└─────── array element type u16 └──────── array type
推荐的反混淆:
mycrate::example::<[u16; 8]>
绑定器
binder →
G
base-62-number
一个 binder 表示要绑定的 高阶特征边界 生命周期的数量。它包含字符 G
后面跟着一个 base-62-number。解码时应将 base-62-number 的值加 1(这样 _
的 base-62-number 编码就被解释为具有 1 个绑定器)。
然后,一个 lifetime 规则可以引用这些编号的生命周期。最小的索引表示最里面的生命周期。绑定生命周期的数量是 base-62-number 的值加一。
例如,在 for<'a, 'b> fn(for<'c> fn (...))
中,...
中的任何 lifetime(但不在更多绑定器内部)将观察索引 1、2 和 3 分别引用 'c
、'b
和 'a
。
推荐的解码
一个 binder 可以使用
for<…>
语法打印,列出 lifetime 中推荐的生命周期。有关示例,请参见 lifetime。
反向引用
backref →
B
base-62-number
一个 backref 用于引用混淆符号的先前部分。这提供了一种简单的压缩形式,以减少混淆符号的长度。这可以帮助减少编译器、链接器和加载器所需的工量和资源。
它包含字符 B
后面跟着一个 base-62-number。该数字表示从符号的 _R
前缀之后的字节开始的 0 索引偏移量。backref 表示从该位置开始的对应元素。
backref 始终引用 backref 本身之前的某个位置。
backref 压缩依赖于这样一个事实,即所有可替换的符号元素都具有自终止的混淆形式。给定编码节点的起始位置,语法保证始终明确节点的结束位置。这是通过不允许在可替换的产生式末尾使用可选或重复元素来确保的。
推荐的解码
应通过渲染 backref 指向的元素来反混淆 backref。在处理深度嵌套的反向引用时,应谨慎考虑,以避免使用过多的堆栈。
示例
fn main() { example::<Example, Example>(); } struct Example; pub fn example<T, U>() {}
函数
example
的符号为_RINvCs7qp2U7fqm6G_7mycrate7exampleNtB2_7ExampleBw_EB2_ │├┘ │├┘ │├┘ ││ ││ ││ ││ ││ │└── backref to offset 3 (crate-root) ││ ││ └─── backref for instantiating-crate path ││ │└────── backref to offset 33 (path to Example) ││ └─────── backref for second generic-arg │└───────────────── backref to offset 3 (crate-root) └────────────────── backref for first generic-arg (first segment of Example path)
推荐的反混淆:
mycrate::example::<mycrate::Example, mycrate::Example>
实例化 crate
instantiating-crate → path
instantiating-crate 是 symbol-name 的一个可选元素,可用于指示哪个 crate 正在实例化该符号。它包含一个 path。
这有助于区分否则相同的符号,例如,来自外部 crate 的函数的单态化如果另一个 crate 也使用相同的类型实例化了相同的泛型函数,则可能会导致重复。
实际上,实例化 crate 通常也是定义符号的 crate,因此它通常被编码为指向符号中其他位置编码的 crate-root 的 backref。
推荐的解码
instantiating-crate 通常不需要显示。
示例
std::path::Path::new("example");
来自
mycrate
crate 实例化的Path::new::<str>
的符号是_RINvMsY_NtCseXNvpPnDBDp_3std4pathNtB6_4Path3neweECs7qp2U7fqm6G_7mycrate └──┬───┘ │ └── instantiating crate identifier `mycrate`
推荐的反混淆:
<std::path::Path>::new::<str>
供应商特定的后缀
vendor-specific-suffix → (
.
|$
) suffixsuffix → {byte}
vendor-specific-suffix 是 symbol-name 末尾的一个可选元素。它包含一个 .
或 $
字符,后面跟着零个或多个字节。对句点或美元符号后面的字符没有限制。
此后缀根据需要由实现添加。这可能发生的一个示例是,当本地唯一名称需要变为全局唯一时。LLVM 可以在 LTO 期间追加 .llvm.<numbers>
后缀以确保唯一名称,而 $
可用于 Mach-O 上的线程本地数据。在这些情况下,通常可以忽略后缀;带后缀的名称与原始名称具有相同的语义。
推荐的解码
vendor-specific-suffix 通常不需要显示。
示例
use std::cell::RefCell; thread_local! { pub static EXAMPLE: RefCell<u32> = RefCell::new(1); }
macOS 上
EXAMPLE
的符号可能具有以下用于线程本地数据的符号_RNvNvNvCs7qp2U7fqm6G_7mycrate7EXAMPLE7___getit5___KEY$tlv$init └───┬───┘ │ └── vendor-specific-suffix
推荐的反混淆:
mycrate::EXAMPLE::__getit::__KEY
通用规则
decimal-number →
0
| non-zero-digit {digit}non-zero-digit →
1
|2
|3
|4
|5
|6
|7
|8
|9
digit →0
| non-zero-digitlower →
a
|b
|c
|d
|e
|f
|g
|h
|i
|j
|k
|l
|m
|n
|o
|p
|q
|r
|s
|t
|u
|v
|w
|x
|y
|z
upper →
A
|B
|C
|D
|E
|F
|G
|H
|I
|J
|K
|L
|M
|N
|O
|P
|Q
|R
|S
|T
|U
|V
|W
|X
|Y
|Z
一个 decimal-number 被编码为一个或多个 digit,表示十进制的数值。
值零被编码为单个字节 0
。注意,在某些情况下,0
后面可能跟着另一个数字,该数字不应被解码为 decimal-number 的一部分。例如,在 nested-path 内的零长度 identifier,而该 nested-path 又在另一个 nested-path 内,将导致两个 identifier 连续出现,其中第一个 identifier 只有 0
的编码。
一个 digit 是一个 ASCII 数字。
一个 lower 和 upper 分别是 ASCII 小写字母和大写字母。
base-62-number
base-62-number → { digit | lower | upper }
_
一个 base-62-number 是一个数值的编码。它使用 ASCII 数字和小写字母和大写字母。该值以 _
字符结尾。如果该值为 0,则编码为 _
字符,没有任何数字。否则,从该值中减去 1,并使用以下映射进行编码
0
-9
映射到 0-9a
-z
映射到 10 到 35A
-Z
映射到 36 到 61
该数字被反复除以 62(使用整数除法,向零取整),以选择序列中的下一个字符。每次除法的余数用于映射以选择下一个字符。重复此操作,直到该数字为 0。然后反转最终的字符序列。
解码是一个类似的逆过程。
示例
值 | 编码 |
---|---|
0 | _ |
1 | 0_ |
11 | a_ |
62 | Z_ |
63 | 10_ |
1000 | g7_ |
符号语法摘要
以下是符号语法的所有产生式的摘要。
symbol-name →
_R
decimal-numberopt path instantiating-crateopt vendor-specific-suffixoptpath →
crate-root
| inherent-impl
| trait-impl
| trait-definition
| nested-path
| generic-args
| backrefcrate-root →
C
identifier
inherent-impl →M
impl-path type
trait-impl →X
impl-path type path
trait-definition →Y
type path
nested-path →N
namespace path identifier
generic-args →I
path {generic-arg}E
identifier → disambiguatoropt undisambiguated-identifier
undisambiguated-identifier →u
opt decimal-number_
opt bytes
bytes → {UTF-8 bytes}disambiguator →
s
base-62-numberimpl-path → disambiguatoropt path
type →
基本类型
| 数组类型
| 切片类型
| 元组类型
| 引用类型
| 可变引用类型
| 常量指针类型
| 可变指针类型
| 函数类型
| 动态特征类型
| 路径
| backrefbasic-type → lower
array-type →A
type const
slice-type →S
type
元组类型 →T
{类型}E
引用类型 →R
生命周期opt 类型
可变引用类型 →Q
生命周期opt 类型
常量指针类型 →P
类型
可变指针类型 →O
类型
函数类型 →F
函数签名
动态特征类型 →D
动态边界 生命周期十六进制数字 → 数字 |
a
|b
|c
|d
|e
|f
函数签名 → 绑定器opt
U
opt (K
ABI)opt {类型}E
类型动态边界 → 绑定器opt {动态特征}
E
动态特征 → 路径 {动态特征关联绑定}
动态特征关联绑定 →p
无歧义标识符 类型特定供应商后缀 → (
.
|$
) 后缀
后缀 → {字节}decimal-number →
0
| non-zero-digit {digit}base-62-number → { digit | lower | upper }
_
non-zero-digit →
1
|2
|3
|4
|5
|6
|7
|8
|9
digit →0
| non-zero-digit
lower →a
|b
|c
|d
|e
|f
|g
|h
|i
|j
|k
|l
|m
|n
|o
|p
|q
|r
|s
|t
|u
|v
|w
|x
|y
|z
upper →A
|B
|C
|D
|E
|F
|G
|H
|I
|J
|K
|L
|M
|N
|O
|P
|Q
|R
|S
|T
|U
|V
|W
|X
|Y
|Z
Rust 实体编码
以下是 Rust 实体在符号中编码的指南。编译器在实体编码方式上有一定的自由度,只要符号是明确的即可。
-
命名函数、方法和静态变量应由 路径 产生式表示。
-
路径应以最内层的实体为根,该实体可以作为路径根。根可以是库 ID、内在实现、特征实现以及(对于默认方法中的项目)特征定义。
-
编译器可以从保留范围内自由选择歧义索引和命名空间标签,只要它确定标识符是明确的即可。
-
等于默认值的泛型参数不应编码,以节省空间。