最大限度地减小锈蚀二进制大小

2020-06-12 15:40:41

默认情况下,Rust针对执行速度而不是二进制大小进行优化,因为这对于绝大多数应用程序来说是理想的。但是对于开发人员想要针对二进制大小进行优化的情况,Rust提供了实现这一目标的机制。

默认情况下,Cargo Build在调试模式下构建Rust二进制文件。调试模式禁用许多优化,这有助于调试器(以及运行它们的IDE)提供更好的调试体验。调试二进制文件可以比发布二进制文件大30%或更多。

在Linux和MacOS上,默认情况下,元件信息包含在编译的.elf文件中。正确执行二进制文件不需要此信息。要删除此信息,请对.elf文件运行strip:

对于发布版本,Cargo将其优化级别默认为3,这将优化二进制文件的速度。要指示Cargo优化为最小二进制大小,请使用Cargo.toml中的z优化级别:

默认情况下,Cargo指示单独编译和优化编译单元。LTO指示链接器在链接阶段进行优化。例如,这可以删除死代码,并且通常会减小二进制大小。

从Rust 1.32开始,默认情况下会删除jemalloc。如果使用Rust 1.32或更高版本,则不需要执行任何操作来减小与此功能相关的二进制大小。

在Rust 1.32之前,为了提高某些平台上的性能,Rust捆绑了jemalloc,这是一个性能经常优于默认系统分配器的分配器。但是,捆绑jemalloc在生成的二进制文件中增加了大约200KB。

要删除Rust 1.28-Rust 1.31上的jemalloc,请将以下代码添加到main.rs的顶部:

默认情况下,Cargo为发布版本指定了16个并行编解码器单元,这缩短了编译时间,但阻止了某些优化。

注意:到目前为止,所讨论的减小二进制大小的特性对程序的行为没有影响(只影响它的执行速度)。此功能对行为没有影响。

默认情况下,当Rust代码遇到必须调用Panic!()的情况时,它会展开堆栈并生成有用的回溯。然而,展开代码确实需要额外的二进制大小。可以指示rustc立即中止,而不是展开,这样就不需要额外的展开代码了。

注意:Xargo目前处于维护状态,但最终下面使用的功能应该会应用到货物中。

Rust附带了标准库(Libstd)的预构建副本及其工具链。这意味着开发人员不需要在每次构建应用程序时都构建libstd。相反,libstd静态链接到二进制文件。

虽然这非常方便,但如果开发人员试图积极优化大小,也有几个缺点。

无法删除特定应用程序中未使用的部分libstd(例如LTO和死机行为)。

这就是萨尔戈的用武之地。Xargo能够从源代码用您的应用程序编译libstd。它通过rustup方便地提供的ruust-src组件来实现这一点。

将Xargo.toml文件添加到项目的根目录(这不会取代Cargo.toml,只是补充):

$rustup工具链夜间安装$rustup覆盖设置夜间$rustup组件添加rust-src$Cargo安装xargo。

#查找您的主机的目标三重。$rustc-vv.主机:x86_64-apple-darwin#使用Xargo构建时使用目标三元组。$xargo build--target x86_64-apple-Darwin--release。

记住要去掉生成的可执行文件。在MacOS上,最终的二进制大小减少到51KB。

即使在Cargo.toml中指定了Panic=ABORT,默认情况下,rustc仍然会在最终二进制文件中包含恐慌字符串和格式化代码。已将不稳定的PARGIC_IMMEDIATE_ABORT功能合并到夜间rustc编译器中以解决此问题。

要使用此命令,请重复上述说明以使用Xargo,但请改用以下Xargo.toml:

记住要去掉生成的可执行文件。在MacOS上,最终的二进制大小减少到30KB。

到目前为止,我们还没有限制从libstd使用的实用程序。在本节中,我们将限制libstd的使用,以进一步减小二进制大小。

如果您想要小于20KB的可执行文件,则必须删除Rust的字符串格式化代码CORE::FMT。PARGIC_IMMEDIATE_ABORT仅删除此代码的某些用法。在某些情况下,还有很多其他代码使用格式化。这包括libstd中的Rust';s&34;Pre-Main代码。

通过使用C入口点(通过添加#![NO_MAIN]属性),手动管理stdio,并仔细分析您或您的依赖项包括哪些代码块,您有时可以利用libstd,同时避免臃肿的core::fmt。

预计代码将是粗制滥造和不可移植的,具有比平常更多的不安全{}。感觉像是no_std,但是有libstd。

从一个空的可执行文件开始,确保xargo膨胀--发布--目标=.。不包含core::fmt或有关填充的内容。添加(取消注释)一点。看到那个沙戈肿胀现在报道的更多了。查看您刚刚添加的源代码。可能使用了一些外部机箱或新的libstd函数。在您的审查过程中递归到这一点(它需要[替换]货物依赖项,可能还需要挖掘libstd),找出为什么它比应有的重量更重。选择替代方式或补丁依赖,以避免不必要的功能。取消注释更多的代码,调试爆炸大小的xargo膨胀,等等。

在此之前,我们的应用程序使用的是Rust标准库libstd。libstd提供了许多方便的、经过良好测试的跨平台API和数据类型。但是,如果用户想要将二进制大小减少到等效的C程序大小,则可以只依赖libc。

了解这种方法有很多缺点是很重要的。首先,您可能需要编写很多不安全的代码,并且无法访问大多数依赖libstd的铁锈碎屑。不过,这是减小二进制大小的一种(尽管极端)选择。

#![no_std]#![no_main]extern crate libc;#[no_manger]pub extern";C";fn main(_argc:isize,_argv:*const*const U8)->;isize{//因为我们传递的是C字符串,所以最后一个空字符是必需的。const Hello:&;&39;static str=";Hello,world!\n\0";;unsafe{libc::printf(Hello。as_ptr()as*const_);}0}#[PARGIC_HANDLER]FN MY_PARGIC(_INFO:&;CORE::PARGIC::PanicInfo)->;!{loop{}}