v0 符号格式

v0 修饰格式在 RFC 2603 中引入。它具有以下特性

  • 它为最终出现在二进制文件符号表中的所有内容提供了一种明确的字符串编码。
  • 它以可逆的方式对泛型参数信息进行编码。
  • 修饰后的符号是可解码的,因此解码后的形式应该很容易识别为例如多态函数的某个具体实例。
  • 它有一个一致的定义,不依赖于某些语言结构的漂亮打印。
  • 符号可以被限制为仅包含字符A-Za-z0-9_。这有助于确保它与平台无关,在某些上下文中其他字符可能具有特殊含义(例如,MSVC DEF 文件中的.)。可选地支持 Unicode 符号。
  • 它试图保持效率,避免不必要的长名称,并避免解码时进行计算量大的操作。

v0 格式不打算与其他修饰方案(如 C++)兼容。

v0 格式不被视为 Rust 的稳定 ABI。目前,这种格式的目的是定义得足够好,以便解码器可以生成符号的合理的人类可读形式。有一些实现定义的部分导致无法完全预测给定的 Rust 实体将如何编码。

以下部分定义了 v0 符号的编码。符号没有标准化的解码形式,但提供了有关如何解码符号的建议。实现者可以选择以不同的方式进行解码。

扩展

将来可能会扩展此格式,以在 Rust 扩展了新的语言项时添加新的标签。为了保持向前兼容性,解码器应该能够优雅地处理在编码中遇到本文件中未描述的标签字符的符号。例如,它们可以回退到显示修饰后的符号。可以在任何包含标签字符的地方扩展格式,例如 类型 规则。现有标签和编码的含义不会改变。

语法表示法

编码符号的格式以扩展的类似 BNF 的语法形式的上下文无关语法来表示。可以在 符号语法摘要 中找到一个综合的摘要。

名称语法示例描述
规则A → B C一个产生式。
串联空格A → B C D从左到右按顺序排列的各个元素。
备选|A → B | C匹配其中一个或另一个。
分组()A → B (C | D) E将多个元素作为一个元素进行分组。
重复{}A → {B}重复封闭元素零次或多次。
选项optA → Bopt C一个可选元素。
字面量等宽字体A → G一个匹配确切字符(区分大小写)的终结符。

符号名称

symbol-name → _R decimal-numberopt path instantiating-crateopt vendor-specific-suffixopt

修饰后的符号以两个字符_R 开头,这是一个前缀,用于标识符号为 Rust 符号。前缀后可以可选地跟着一个decimal-number,它指定编码版本。此数字目前未使用,并且在当前编码中从未出现。接下来是一个path,它对实体的路径进行编码。路径后跟一个可选的instantiating-crate,它有助于消除可能在不同板条箱中多次实例化的实体的歧义。最后部分是一个可选的vendor-specific-suffix

推荐的解码

symbol-name 应该显示为pathinstantiating-cratevendor-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)或单态化项的泛型参数。

初始标签字符可用于确定它表示哪种路径。

标签规则描述
Ccrate-root箱路径的根。
M内联实现内联实现。
X特征实现特征实现。
Y特征定义特征定义。
N嵌套路径嵌套路径。
I泛型参数泛型参数。
B反向引用反向引用。

路径:箱根

箱根 → C 标识符

箱根表示指向箱模块树根的路径。它由字符 C 后跟箱名称作为 标识符 组成。

箱名称是从定义箱中看到的名称。由于 Rust 支持将多个具有相同名称的箱链接起来,因此使用 消歧符 来使名称在整个箱图中唯一。

推荐的解码

箱根可以显示为标识符,例如 mycrate

通常,标识符中的消歧符不需要显示,但作为备用形式,消歧符可以以十六进制显示,例如 mycrate[ca63f166dbe9294]

示例

fn example() {}

mycrateexample 的符号是

_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 类型

内联实现表示指向内联实现的路径。它由字符 M 后跟 impl-path 组成,它唯一标识了定义项的 impl 块。之后是一个 类型,表示 impl 的 Self 类型。

推荐的解码

内联实现可以显示为指向 类型 的限定路径段,位于尖括号内。impl-path 通常不需要显示。

示例

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 类型 路径

特征实现表示指向特征实现的路径。它由字符 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 → 消歧符opt 路径

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 类型 路径

特征定义是指向特征定义的路径。它由字符 Y 后跟 类型 组成,它是引用者的 Self 类型,然后是 路径 到特征定义。

推荐的解码

特征定义可以显示为使用 < 类型 as 路径 > 语法的限定路径段。

示例

trait Trait {
    fn example() {}
}
struct Example;
impl Trait for Example {}

Example 实现的特征 Traitexample 的符号是

_RNvYNtCs15kBYyAo9fc_7mycrate7ExampleNtB4_5Trait7exampleB4_
    │└──────────────┬───────────────┘└────┬────┘
    │               │                     │
    │               │                     └── path to the trait "Trait"
    │               └──────────────────────── path to the implementing type "mycrate::Example"
    └──────────────────────────────────────── trait-definition

推荐反混淆:<mycrate::Example as mycrate::Trait>::example

路径:嵌套路径

嵌套路径 → N 命名空间 路径 标识符

嵌套路径是表示可选命名实体的路径。它由字符 N 后跟 命名空间 组成,表示实体的命名空间,然后是 路径,它表示实体的父级,最后是实体的 标识符

当实体未命名时,实体的标识符的长度可能为 0。例如,闭包、元组式结构体构造函数和匿名常量可能没有名称。除非消歧符为 0,否则标识符可能仍然具有消歧符。

推荐的解码

嵌套路径可以通过首先显示 路径,然后显示 :: 分隔符,最后显示 标识符 来显示。如果 标识符 为空,则不应显示分隔符 ::

如果指定了 命名空间,则可能会添加额外的上下文,例如

路径 ::{ 命名空间 (: 标识符)opt # 消歧符作为十进制数 }

这里,命名空间 C 可以打印为 closureS 可以打印为 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

泛型参数 →

      生命周期

   | 类型

   | K 常量

泛型参数是表示泛型参数列表的路径。它由字符 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 来处理隐式调用者位置。

推荐的解码

有关推荐的反混淆,请参见 嵌套路径

标识符

标识符 → 消歧符opt 未消歧标识符

未消歧标识符 → uopt 十进制数 _opt 字节

字节 → {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 编码中的- 字符将被替换为_

以下是一些示例

原始PunycodePunycode + 编码
føøf-5gaaf_5gaa
α_ω_-ylb7e__ylb7e
铁锈n84amfn84amf
🤦fq9hfq9h
ρυστ2xaedc2xaedc

注意:是否使用 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-typemut-ref-typedyn-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 →

      类型 常量数据

   | p

   | backref

const-data → nopt {十六进制数字} _

十六进制数字数字 | a | b | c | d | e | f

常量 用于编码泛型和类型中使用的常量值。它具有以下形式

  • 编码为 类型 的常量值,表示常量的类型,以及 常量数据,表示常量值,后面跟着_ 以终止 常量
  • 表示 占位符 的字符p
  • 对先前编码的具有相同值的 常量反向引用

常量数据 的编码取决于类型

  • bool — 值false 编码为0_,值 true 编码为1_
  • char — 字符的 Unicode 标量值以十六进制编码。
  • 无符号整数 — 值以十六进制编码。
  • 有符号整数 — 字符n 是一个前缀,表示它是负数,后面跟着以十六进制编码的绝对值。

推荐的解码

常量 可以根据类型显示为常量值。

p 占位符应显示为_ 字符。

对于特定类型

  • b (bool) — 显示为truefalse
  • 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 不会被类型或常量参数TN 单态化。这些将使用占位符来表示这些泛型参数。它的符号为

_RNvNvMCsd9PVOYlP1UU_7mycrateINtB4_7ExamplepKpE3foo14EXAMPLE_STATIC
                             │             │││
                             │             ││└── const placeholder
                             │             │└─── const generic argument
                             │             └──── type placeholder
                             └────────────────── generic-args

推荐的反混淆:<mycrate::Example<_, _>>::foo::EXAMPLE_STATIC

类型

type →

      基本类型

   | 数组类型

   | 切片类型

   | 元组类型

   | 引用类型

   | 可变引用类型

   | 常量指针类型

   | 可变指针类型

   | 函数类型

   | 动态特征类型

   | 路径

   | backref

类型 表示 Rust 类型。初始字符可用于区分编码的类型。基于初始标记字符的类型编码如下

  • 基本类型 编码为单个字符
    • ai8
    • bbool
    • cchar
    • df64
    • estr
    • ff32
    • hu8
    • iisize
    • jusize
    • li32
    • mu32
    • ni128
    • ou128
    • si16
    • tu16
    • u — 单位()
    • v — 可变参数...
    • xi64
    • yu64
    • z!
    • p占位符 _

其余的基元编码为箱子生成,例如C4f128

  • A数组 [T; N]

    数组类型A 类型 常量

    标记A 后面跟着数组的 类型,后面跟着数组大小的 常量

  • S切片 [T]

    切片类型S 类型

    标记S 后面跟着切片的 类型

  • T元组 (T1, T2, T3, ...)

    元组类型T {类型} E

    标记T 后面跟着一个或多个 类型,表示每个字段的类型,后面跟着一个终止符E 字符。

    注意,零长度元组(单位)使用u 基本类型 编码。

  • R引用 &T

    引用类型R 生命周期opt 类型

    标记R 后面跟着一个可选的 生命周期,后面跟着引用的 类型。如果生命周期已被擦除,则不包括它。

  • Q可变引用 &mut T

    可变引用类型Q 生命周期opt 类型

    标记Q 后面跟着一个可选的 生命周期,后面跟着可变引用的 类型。如果生命周期已被擦除,则不包括它。

  • P常量原始指针 *const T

    标记P 后面跟着指针的 类型

    常量指针类型P 类型

  • O可变原始指针 *mut T

    可变指针类型O 类型

    标记O 后面跟着指针的 类型

  • F函数指针 fn(…) -> …

    函数类型F 函数签名

    函数签名绑定器opt Uopt (K ABI)opt {类型} E 类型

    ABI

          C

       | 未消歧标识符

    标记F 后面跟着函数签名的 函数签名函数签名 是函数指针的签名。

    它以一个可选的 绑定器 开头,表示更高阶的特征边界 (for<…>)。

    后面跟着一个可选的U 字符,它存在于unsafe 函数中。

    后面跟着一个可选的K 字符,它表示指定了 ABI。如果未指定 ABI,则假定为"Rust" ABI。

    ABI 可以是字母C,表示它是"C" ABI。否则,它是一个 未消歧标识符,表示 ABI 字符串,其中连字符已转换为下划线。

    后面跟着零个或多个 类型,表示函数的输入参数。

    后面跟着字符E,然后是返回值的 类型

  • 一个指向命名类型的 path

  • 一个 backref 用于引用之前编码的类型。

推荐的解码

一个 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-cratesymbol-name 的一个可选元素,可用于指示哪个 crate 正在实例化该符号。它包含一个 path

这有助于区分否则相同的符号,例如,来自外部 crate 的函数的单态化如果另一个 crate 也使用相同的类型实例化了相同的泛型函数,则可能会导致重复。

实际上,实例化 crate 通常也是定义符号的 crate,因此它通常被编码为指向符号中其他位置编码的 crate-rootbackref

推荐的解码

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 → (. | $) suffix

suffix → {byte}

vendor-specific-suffixsymbol-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-digit1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

digit0 | non-zero-digit

lowera |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

upperA | 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 数字。

一个 lowerupper 分别是 ASCII 小写字母和大写字母。

base-62-number

base-62-number → { digit | lower | upper } _

一个 base-62-number 是一个数值的编码。它使用 ASCII 数字和小写字母和大写字母。该值以 _ 字符结尾。如果该值为 0,则编码为 _ 字符,没有任何数字。否则,从该值中减去 1,并使用以下映射进行编码

  • 0-9 映射到 0-9
  • a-z 映射到 10 到 35
  • A-Z 映射到 36 到 61

该数字被反复除以 62(使用整数除法,向零取整),以选择序列中的下一个字符。每次除法的余数用于映射以选择下一个字符。重复此操作,直到该数字为 0。然后反转最终的字符序列。

解码是一个类似的逆过程。

示例

编码
0_
10_
11a_
62Z_
6310_
1000g7_

符号语法摘要

以下是符号语法的所有产生式的摘要。

symbol-name_R decimal-numberopt path instantiating-crateopt vendor-specific-suffixopt

path

      crate-root

   | inherent-impl

   | trait-impl

   | trait-definition

   | nested-path

   | generic-args

   | backref

crate-rootC identifier

inherent-implM impl-path type

trait-implX impl-path type path

trait-definitionY type path

nested-pathN namespace path identifier

generic-argsI path {generic-arg} E

identifierdisambiguatoropt undisambiguated-identifier

undisambiguated-identifieruopt decimal-number _opt bytes

bytes → {UTF-8 bytes}

disambiguators base-62-number

impl-pathdisambiguatoropt path

type

      基本类型

   | 数组类型

   | 切片类型

   | 元组类型

   | 引用类型

   | 可变引用类型

   | 常量指针类型

   | 可变指针类型

   | 函数类型

   | 动态特征类型

   | 路径

   | backref

basic-typelower

array-typeA type const

slice-typeS type

元组类型T {类型} E

引用类型R 生命周期opt 类型

可变引用类型Q 生命周期opt 类型

常量指针类型P 类型

可变指针类型O 类型

函数类型F 函数签名

动态特征类型D 动态边界 生命周期

命名空间小写 | 大写

泛型参数

      生命周期

   | 类型

   | K 常量

生命周期L 62进制数字

常量

      类型 常量数据

   | p

   | backref

常量数据nopt {十六进制数字} _

十六进制数字数字 | a | b | c | d | e | f

函数签名绑定器opt Uopt (K ABI)opt {类型} E 类型

ABI

      C

   | 未消歧标识符

动态边界绑定器opt {动态特征} E

动态特征路径 {动态特征关联绑定}

动态特征关联绑定p 无歧义标识符 类型

绑定器G 62进制数字

反向引用B 62进制数字

实例化库路径

特定供应商后缀 → (. | $) 后缀

后缀 → {字节}

decimal-number

      0

   | non-zero-digit {digit}

base-62-number → { digit | lower | upper } _

non-zero-digit1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

digit0 | non-zero-digit

lowera |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

upperA | 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、内在实现、特征实现以及(对于默认方法中的项目)特征定义。

  • 编译器可以从保留范围内自由选择歧义索引和命名空间标签,只要它确定标识符是明确的即可。

  • 等于默认值的泛型参数不应编码,以节省空间。