1 个不稳定版本
0.1.0 | 2024年6月7日 |
---|
#895 在 解析器实现 中
20KB
268 行
重复符号检查器
此工具旨在确定编译的Rust二进制文件中有多少是由相同的函数组成的。
它依赖于调试符号来查找函数,因此您的二进制文件不能被剥离。
它通过读取每个函数的指令,对这些指令进行归一化,以适应仅由于函数的基本地址而产生的差异,然后根据结果指令字节进行分组。
目前它仅支持x86_64二进制文件,并且仅在Linux上进行了测试。
已识别的重复函数有几个不同的来源
- 即使函数来自代码库的不同部分,它们也可能偶然相同。
- 通用函数在单态化后,即使具有不同的通用参数也可能相同。例如,函数可能仅依赖于通用参数的大小,这意味着所有具有相同大小的类型的单态化最终都会相等。
- 通用函数可能多次使用相同的参数进行单态化,但后来并没有进行去重。
最后一个是我在乎的。不幸的是,目前很难区分这最后两种情况。Rustc似乎有时会在符号名称中包含通用参数,但通常它会在符号名称的末尾使用不同的哈希值。
推荐用法
cargo run --release -- --verbose --demangle /path/to/bin
样本输出
现在我将展示一些从运行ripgrep发布构建的该工具的样本输出。由于输出相当长,我没有包含所有输出,只包含一些值得进一步讨论的部分。
Function size: 103
Copies: 21
Excess bytes: 2060
Names:
1x `alloc::sync::Arc<T,A>::drop_slow::hd706a4fa915b4d89`
1x `alloc::sync::Arc<T,A>::drop_slow::h87093f1f9dea2d0e`
1x `alloc::sync::Arc<T,A>::drop_slow::h10d0dcce72958fd8`
1x `alloc::sync::Arc<T,A>::drop_slow::h8c617ac2d907e2aa`
1x `alloc::sync::Arc<T,A>::drop_slow::hd71eeda01817a536`
1x `alloc::sync::Arc<T,A>::drop_slow::he520a5dd6ca64703`
1x `alloc::sync::Arc<T,A>::drop_slow::h628a33a33ecac575`
1x `alloc::sync::Arc<T,A>::drop_slow::h8cec9ca0439c3711`
1x `alloc::sync::Arc<T,A>::drop_slow::h4cd5ea407012db46`
1x `alloc::sync::Arc<T,A>::drop_slow::h224d6f2371018a1c`
1x `alloc::sync::Arc<T,A>::drop_slow::h990f0b7fc7e3af11`
1x `alloc::sync::Arc<T,A>::drop_slow::h73ba588d5943ac7a`
1x `alloc::sync::Arc<T,A>::drop_slow::h2dc0bbd1c9c62e26`
1x `alloc::sync::Arc<T,A>::drop_slow::h517054e3fb2dbac5`
1x `alloc::sync::Arc<T,A>::drop_slow::hdf2cd5f474fa2393`
1x `alloc::sync::Arc<T,A>::drop_slow::h61f7d6c3b84da1e9`
1x `alloc::sync::Arc<T,A>::drop_slow::h2487201382634f65`
1x `alloc::sync::Arc<T,A>::drop_slow::hfd3d412a64e719d1`
1x `alloc::sync::Arc<T,A>::drop_slow::h127b8fb68b2d8622`
1x `alloc::sync::Arc<T,A>::drop_slow::h545a994a083aa1dc`
1x `alloc::sync::Arc<T,A>::drop_slow::he1370168d3fba403`
这里我们可以看到有21个删除Arc的函数副本。我们不知道Arc里有什么。很可能每个这些函数都是为了删除具有不同 Arc<T>
的不同 T
而创建的,但机器代码最终是相同的。
Function size: 236
Copies: 26
Excess bytes: 5900
Names:
3x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h0ede7a90cb4e4caf`
3x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h3dc26697a761e8f9`
3x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h3801b4f9aaad7fc2`
5x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h9eb8ddad156565f6`
5x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::hc517e495ab2a88a4`
4x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::hde4f9e64fcdf6bea`
3x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::hd22a22559e751911`
这里我们有7个不同的函数名,它们之间的区别仅在于它们的哈希值。编译器已经替换了类型参数,因此我们知道这些函数都在丢弃相同的类型。每个函数名然后都有几个副本。这些额外的符号副本来自不同的代码生成单元。我们可以通过重建具有 codegen-units=1
的二进制文件来确定这一点,然后我们得到以下内容
Function size: 236
Copies: 7
Excess bytes: 1416
Names:
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h92c782dcb35669b7`
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h67d0912fdfd31563`
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h11e9189330999400`
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h3242a4cd1700d56b`
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h17293025138ff46d`
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h085ce04cd90a2c3f`
1x `core::ptr::drop_in_place<regex_automata::meta::wrappers::PikeVMCache>::h419132e2cb015325`
当编译不同的crate时,这些7个副本都被单形化了。我们可以通过运行没有 --demangle
的工具,然后使用grep定位包含该符号的 .rlib 来验证这一点。这些符号中的每一个都出现在不同的rlib中。
典型结果
对于使用 rustc 1.78.0 编译的 ripgrep 的发布版本,我观察到以下情况
配置 | % 重复函数 |
---|---|
默认发布 | 5.8 |
禁用LTO的发布 | 6.4 |
带有胖LTO的发布 | 2.1 |
codegen-units=1的发布 | 1.9 |
codegen-units=1 + 胖LTO的发布 | 1.8 |
codegen-units=1 + 胖LTO + -Zshare-generics的发布 | 0.9 |
默认调试 (-Zshare-generics 默认开启) | 6.6 |
默认调试带有 -Zshare-generics=off | 7.1 |
如果尝试重现这些数字,我测试了 commit 601e122e9f。
对于具有更多依赖项的二进制文件,我以自己的crate Rust REPL evcxr为例。在其发布版本中,10%的可执行字节来自重复函数的额外副本。通过 codegen-units=1
,这降低到1%。
许可证
根据您的选择,许可协议为 Apache License, Version 2.0 或 MIT license。
除非您明确说明,否则您提交给 Wild 的任何贡献,根据 Apache-2.0 许可证定义,将按照上述方式双许可,无需任何额外的条款或条件。
依赖项
~18–30MB
~475K SLoC