diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a24d209 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*] +max_line_length = 100 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2ec948..cdcc0b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,49 +8,72 @@ jobs: strategy: matrix: platform: [ ubuntu-latest, macos-latest, windows-latest ] - toolchain: [ stable, 1.28.0, nightly ] + toolchain: [ stable, nightly ] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - override: true - - uses: actions-rs/cargo@v1 - with: - command: build - args: --release --all-features + - name: Build + run: cargo build --release --all-features no_std: name: no_std strategy: matrix: platform: [ ubuntu-latest, macos-latest ] - toolchain: [ stable, 1.30.0, nightly ] + toolchain: [ stable, nightly ] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + - name: Build + working-directory: no_std/nix + run: cargo build --release --all-features + no_std_win: + name: no_std-win + strategy: + matrix: + toolchain: [ stable, nightly ] + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + - name: Build + working-directory: no_std/win + run: cargo build --release --all-features + no_main_win: + name: no_main-win + strategy: + matrix: + toolchain: [ stable, nightly ] + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.toolchain }} - override: true - name: Build - working-directory: no_std + working-directory: no_main/win run: cargo build --release --all-features build_std: name: build-std strategy: matrix: - project_dir: [ build_std, no_main ] + project_dir: [ build_std, no_main/nix ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: toolchain: nightly - override: true - name: Build working-directory: ${{ matrix.project_dir }} run: > rustup component add rust-src; - cargo +nightly build -Z build-std=std,panic_abort --target x86_64-unknown-linux-gnu --release; - cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu --release; + RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features="optimize_for_size" --target x86_64-unknown-linux-gnu --release; + RUSTFLAGS="-Zunstable-options -Cpanic=immediate-abort" cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features= --target x86_64-unknown-linux-gnu --release; diff --git a/Cargo.toml b/Cargo.toml index 1db9e9d..cb87ee9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "min-sized-rust" version = "0.1.0" -authors = ["johnthagen "] +edition = "2021" license-file = "LICENSE.txt" [dependencies] diff --git a/LICENSE.txt b/LICENSE.txt index 6fc1709..d4e7692 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2018-2021 John Hagen +Copyright (c) 2018-2022 John Hagen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. \ No newline at end of file +IN THE SOFTWARE. diff --git a/README.md b/README.md index 9921627..f939890 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,6 @@ [github-actions-badge]: https://github.com/johnthagen/min-sized-rust/workflows/build/badge.svg -> To help this project reach more users, consider upvoting the -> [`min-sized-rust` Stack Overflow answer](https://stackoverflow.com/a/54842093/1398841). - This repository demonstrates how to minimize the size of a Rust binary. By default, Rust optimizes for execution speed, compilation speed, and ease of debugging @@ -58,7 +55,7 @@ $ strip target/release/min-sized-rust [Cargo defaults its optimization level to `3` for release builds][cargo-profile], which optimizes the binary for **speed**. To instruct Cargo to optimize for minimal binary -**size**, use the `z` optimization level in +**size**, use the `z` optimization level in [`Cargo.toml`](https://doc.rust-lang.org/cargo/reference/manifest.html): [cargo-profile]: https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles @@ -68,12 +65,20 @@ which optimizes the binary for **speed**. To instruct Cargo to optimize for mini opt-level = "z" # Optimize for size. ``` +> [!NOTE] +> In some cases the `"s"` level may result in a smaller binary than `"z"`, as explained in the +> [`opt-level` documentation](https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level): +> +> It is recommended to experiment with different levels to find the right balance for your project. +> There may be surprising results, such as ... the `"s"` and `"z"` levels not being necessarily +> smaller. + # Enable Link Time Optimization (LTO) ![Minimum Rust: 1.0](https://img.shields.io/badge/Minimum%20Rust%20Version-1.0-brightgreen.svg) -By default, -[Cargo instructs compilation units to be compiled and optimized in isolation][cargo-profile]. +By default, +[Cargo instructs compilation units to be compiled and optimized in isolation][cargo-profile]. [LTO](https://llvm.org/docs/LinkTimeOptimization.html) instructs the linker to optimize at the link stage. This can, for example, remove dead code and often times reduces binary size. @@ -84,34 +89,20 @@ Enable LTO in `Cargo.toml`: lto = true ``` -# Remove Jemalloc - -![Minimum Rust: 1.28](https://img.shields.io/badge/Minimum%20Rust%20Version-1.28-brightgreen.svg) -![Maximum Rust: 1.31](https://img.shields.io/badge/Maximum%20Rust%20Version-1.31-brightgreen.svg) - -As of Rust 1.32, -[`jemalloc` is removed by default](https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html). -**If using Rust 1.32 or newer, no action is needed to reduce binary size regarding this -feature**. - -**Prior to Rust 1.32**, to improve performance on some platforms Rust bundled -[jemalloc](https://github.com/jemalloc/jemalloc), an allocator that often -outperforms the default system allocator. Bundling jemalloc added around 200KB -to the resulting binary, however. +# Dynamic Linking: Why It Doesn't Work -To remove `jemalloc` on Rust 1.28 - Rust 1.31, add this code to the top of `main.rs`: +![Minimum Rust: 1.0](https://img.shields.io/badge/Minimum%20Rust%20Version-1.0-brightgreen.svg) -```rust -use std::alloc::System; +Some might suggest using [`prefer-dynamic`](https://doc.rust-lang.org/rustc/codegen-options/index.html#prefer-dynamic) for smaller binaries, but this approach has critical limitations: -#[global_allocator] -static A: System = System; -``` +- **No stable ABI** - binaries break between Rust versions +- **Deployment complexity** - requires exact library matches +- **Community consensus** - static linking preferred for reliability # Reduce Parallel Code Generation Units to Increase Optimization [By default][cargo-profile], Cargo specifies 16 parallel codegen units for release builds. -This improves compile times, but prevents some optimizations. +This improves compile times, but prevents some optimizations. Set this to `1` in `Cargo.toml` to allow for maximum size reduction optimizations: @@ -124,13 +115,14 @@ codegen-units = 1 ![Minimum Rust: 1.10](https://img.shields.io/badge/Minimum%20Rust%20Version-1.10-brightgreen.svg) -> **Note**: Up to this point, the features discussed to reduce binary size did not have an -impact on the behaviour of the program (only its execution speed). This feature does -have an impact on behavior. +> [!IMPORTANT] +> Up to this point, the features discussed to reduce binary size did not have an +> impact on the behaviour of the program (only its execution speed). This feature does +> have an impact on behavior. -[By default][cargo-profile], when Rust code encounters a situation when it must call `panic!()`, -it unwinds the stack and produces a helpful backtrace. The unwinding code, however, does require -extra binary size. `rustc` can be instructed to abort immediately rather than unwind, which +[By default][cargo-profile], when Rust code encounters a situation when it must call `panic!()`, +it unwinds the stack and produces a helpful backtrace. The unwinding code, however, does require +extra binary size. `rustc` can be instructed to abort immediately rather than unwind, which removes the need for this extra unwinding code. Enable this in `Cargo.toml`: @@ -140,13 +132,46 @@ Enable this in `Cargo.toml`: panic = "abort" ``` +# Remove Location Details + +![Minimum Rust: Nightly](https://img.shields.io/badge/Minimum%20Rust%20Version-nightly-orange.svg) + +By default, Rust includes file, line, and column information for `panic!()` and `[track_caller]` +to provide more useful traceback information. This information requires space in the binary and +thus increases the size of the compiled binaries. + +To remove this file, line, and column information, use the unstable +[`rustc` `-Zlocation-detail`](https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md#location-detail-control) +flag: + +```bash +$ RUSTFLAGS="-Zlocation-detail=none" cargo +nightly build --release +``` + +# Remove `fmt::Debug` + +![Minimum Rust: Nightly](https://img.shields.io/badge/Minimum%20Rust%20Version-nightly-orange.svg) + +With the +[`-Zfmt-debug`](https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/fmt-debug.html) flag +you can turn `#[derive(Debug)]`and +[`{:?}`](https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html) formatting into no-ops. This +will ruin output of `dbg!()`, `assert!()`, `unwrap()`, etc., and may break code that unwisely +relies on the debug formatting, but it will remove derived `fmt` functions and their strings. + +```bash +$ RUSTFLAGS="-Zfmt-debug=none" cargo +nightly build --release +``` + # Optimize `libstd` with `build-std` ![Minimum Rust: Nightly](https://img.shields.io/badge/Minimum%20Rust%20Version-nightly-orange.svg) -> **Note**: See also [Xargo](https://github.com/japaric/xargo), the predecessor to `build-std`. - [Xargo is currently in maintenance status](https://github.com/japaric/xargo/issues/193). +> [!NOTE] +> See also [Xargo](https://github.com/japaric/xargo), the predecessor to `build-std`. +[Xargo is currently in maintenance status](https://github.com/japaric/xargo/issues/193). +> [!NOTE] > Example project is located in the [`build_std`](build_std) folder. Rust ships pre-built copies of the standard library (`libstd`) with its toolchains. This means @@ -158,10 +183,10 @@ aggressively optimize for size. 1. The prebuilt `libstd` is optimized for speed, not size. -2. It's not possible to remove portions of `libstd` that are not used in a particular application +2. It's not possible to remove portions of `libstd` that are not used in a particular application (e.g. LTO and panic behaviour). -This is where [`build-std`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std) +This is where [`build-std`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std) comes in. The `build-std` feature is able to compile `libstd` with your application from the source. It does this with the `rust-src` component that `rustup` conveniently provides. @@ -175,7 +200,7 @@ $ rustup component add rust-src --toolchain nightly Build using `build-std`: ```bash -# Find your host's target triple. +# Find your host's target triple. $ rustc -vV ... host: x86_64-apple-darwin @@ -183,26 +208,36 @@ host: x86_64-apple-darwin # Use that target triple when building with build-std. # Add the =std,panic_abort to the option to make panic = "abort" Cargo.toml option work. # See: https://github.com/rust-lang/wg-cargo-std-aware/issues/56 -$ cargo +nightly build -Z build-std=std,panic_abort --target x86_64-apple-darwin --release +$ RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none" cargo +nightly build \ + -Z build-std=std,panic_abort \ + -Z build-std-features="optimize_for_size" \ + --target x86_64-apple-darwin --release ``` +The `optimize_for_size` flag provides a hint to `libstd` that it should try to use algorithms +optimized for binary size. More information about it can be found in the +[tracking issue](https://github.com/rust-lang/rust/issues/125612). + On macOS, the final stripped binary size is reduced to 51KB. -# Remove `panic` String Formatting with `panic_immediate_abort` +# Remove `panic` String Formatting with `panic=immediate-abort` ![Minimum Rust: Nightly](https://img.shields.io/badge/Minimum%20Rust%20Version-nightly-orange.svg) Even if `panic = "abort"` is specified in `Cargo.toml`, `rustc` will still include panic strings -and formatting code in final binary by default. -[An unstable `panic_immediate_abort` feature](https://github.com/rust-lang/rust/pull/55011) +and formatting code in final binary by default. +[An unstable `panic=immediate-abort` feature](https://github.com/rust-lang/rust/pull/146317) has been merged into the `nightly` `rustc` compiler to address this. -To use this, repeat the instructions above to use `build-std`, but also pass the following -[`-Z build-std-features=panic_immediate_abort`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std-features) -option. +To use this, repeat the instructions above to use `build-std`, but also pass +[`-Zunstable-options -Cpanic=immediate-abort`](https://doc.rust-lang.org/rustc/command-line-arguments.html#-z-set-unstable-options) and +[`-Z build-std-features=`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std-features) (which will disable the default `backtrace` and `panic-unwind` features) +to `rustc`. ```bash -$ cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort \ +$ RUSTFLAGS="-Zunstable-options -Cpanic=immediate-abort" cargo +nightly build \ + -Z build-std=std,panic_abort \ + -Z build-std-features= \ --target x86_64-apple-darwin --release ``` @@ -212,31 +247,30 @@ On macOS, the final stripped binary size is reduced to 30KB. ![Minimum Rust: Nightly](https://img.shields.io/badge/Minimum%20Rust%20Version-nightly-orange.svg) -> Example project is located in the [`no_main`](no_main) folder. +> [!NOTE] +> Example projects are located in the [`no_main`](no_main) folder. -> This section was contributed in part by [@vi](https://github.com/vi) - Up until this point, we haven't restricted what utilities we used from `libstd`. In this section we will restrict our usage of `libstd` in order to reduce binary size further. -If you want an executable smaller than 20 kilobytes, Rust's string formatting code, -[`core::fmt`](https://doc.rust-lang.org/core/fmt/index.html) must -be removed. `panic_immediate_abort` only removes some usages of this code. There is a lot of other +If you want an executable smaller than 20 kilobytes, Rust's string formatting code, +[`core::fmt`](https://doc.rust-lang.org/core/fmt/index.html) must +be removed. `panic=immediate-abort` only removes some usages of this code. There is a lot of other code that uses formatting in some cases. That includes Rust's "pre-main" code in `libstd`. -By using a C entry point (by adding the `#![no_main]` attribute) , managing stdio manually, and -carefully analyzing which chunks of code you or your dependencies include, you can sometimes +By using a C entry point (by adding the `#![no_main]` attribute) , managing stdio manually, and +carefully analyzing which chunks of code you or your dependencies include, you can sometimes make use of `libstd` while avoiding bloated `core::fmt`. -Expect the code to be hacky and unportable, with more `unsafe{}`s than usual. It feels like +Expect the code to be hacky and unportable, with more `unsafe{}`s than usual. It feels like `no_std`, but with `libstd`. -Start with an empty executable, ensure -[`xargo bloat --release --target=...`](https://github.com/RazrFalcon/cargo-bloat) contains no -`core::fmt` or something about padding. Add (uncomment) a little bit. See that `xargo bloat` now -reports drastically more. Review source code that you've just added. Probably some external crate or +Start with an empty executable, ensure +[`xargo bloat --release --target=...`](https://github.com/RazrFalcon/cargo-bloat) contains no +`core::fmt` or something about padding. Add (uncomment) a little bit. See that `xargo bloat` now +reports drastically more. Review source code that you've just added. Probably some external crate or a new `libstd` function is used. Recurse into that with your review process -(it requires `[replace]` Cargo dependencies and maybe digging in `libstd`), find out why it +(it requires `[replace]` Cargo dependencies and maybe digging in `libstd`), find out why it weighs more than it should. Choose alternative way or patch dependencies to avoid unnecessary features. Uncomment a bit more of your code, debug exploded size with `xargo bloat` and so on. @@ -246,7 +280,8 @@ On macOS, the final stripped binary is reduced to 8KB. ![Minimum Rust: 1.30](https://img.shields.io/badge/Minimum%20Rust%20Version-1.30-brightgreen.svg) -> Example project is located in the [`no_std`](no_std) folder. +> [!NOTE] +> Example projects are located in the [`no_std`](no_std) folder. Up until this point, our application was using the Rust standard library, `libstd`. `libstd` provides many convenient, well tested cross-platform APIs and data types. But if a user wants @@ -282,57 +317,142 @@ fn my_panic(_info: &core::panic::PanicInfo) -> ! { # Compress the binary -Up until this point, all size-reducing techniques were Rust-specific. This section describes -a language-agnostic binary packing tool that is an option to reduce binary size further. +> [!NOTE] +> Up until this point, all size-reducing techniques were Rust-specific. This section describes +> a language-agnostic binary packing tool that is an option to reduce binary size further. -[UPX](https://github.com/upx/upx) is a powerful tool for creating a self contained, compressed -binary with no addition runtime requirements. It claims to typically reduce binary size by 50-70%, +[UPX](https://github.com/upx/upx) is a powerful tool for creating a self-contained, compressed +binary with no addition runtime requirements. It claims to typically reduce binary size by 50-70%, but the actual result depends on your executable. ```bash $ upx --best --lzma target/release/min-sized-rust ``` -It should be noted that there have been times that UPX-packed binaries have flagged -heuristic-based anti-virus software because malware often uses UPX. +> [!WARNING] +> There have been times that UPX-packed binaries have flagged heuristic-based antivirus software +> because malware often uses UPX. # Tools -- [`cargo-bloat`](https://github.com/RazrFalcon/cargo-bloat) - Find out what takes most of the +- [`cargo-bloat`](https://github.com/RazrFalcon/cargo-bloat) - Find out what takes most of the space in your executable. +- [`cargo-llvm-lines`](https://github.com/dtolnay/cargo-llvm-lines) - Measure the number and size + of instantiations of each generic function, indicating which parts of your code offer the highest + leverage in improving compilation metrics. +- [`cargo-unused-features`](https://github.com/TimonPost/cargo-unused-features) - Find and prune + enabled but potentially unused feature flags from your project. - [`momo`](https://github.com/llogiq/momo) - `proc_macro` crate to help keeping the code footprint of generic methods in check. - [Twiggy](https://rustwasm.github.io/twiggy/index.html) - A code size profiler for Wasm. # Containers -Sometimes it's advantageous to deploy Rust into containers -(e.g. [Docker](https://www.docker.com/)). There are several great existing resources to help -create minimum sized Docker containers that run Rust binaries. +Sometimes it's advantageous to deploy Rust into containers +(e.g. [Docker](https://www.docker.com/)). There are several great existing resources to help +create minimum sized container images that run Rust binaries. +- [Official `rust:alpine` image](https://hub.docker.com/_/rust) - [mini-docker-rust](https://github.com/kpcyrd/mini-docker-rust) -- [rust-musl-builder](https://github.com/emk/rust-musl-builder) - [muslrust](https://github.com/clux/muslrust) - [docker-slim](https://github.com/docker-slim/docker-slim) - Minify Docker images +- [dive](https://github.com/wagoodman/dive) - A tool for exploring a container image and + discovering ways to shrink the size of the image. +- [distroless](https://github.com/GoogleContainerTools/distroless) - 2MB base image to run statically linked Rust program # References +- [151-byte static Linux binary in Rust - 2015][151-byte-static-linux-binary] - [Why is a Rust executable large? - 2016][why-rust-binary-large] -- [Freestanding Rust Binary - 2018](https://os.phil-opp.com/freestanding-rust-binary/) - [Tiny Rocket - 2018](https://jamesmunns.com/blog/tinyrocket/) - [Formatting is Unreasonably Expensive for Embedded Rust - 2019][fmt-unreasonably-expensive] - [Tiny Windows executable in Rust - 2019][tiny-windows-exe] - [Making a really tiny WebAssembly graphics demos - 2019][tiny-webassembly-graphics] - [Reducing the size of the Rust GStreamer plugin - 2020][gstreamer-plugin] +- [Optimizing Rust Binary Size - 2020][optimizing-rust-binary-size] +- [Minimizing Mender-Rust - 2020][minimizing-mender-rust] +- [Optimize Rust binaries size with cargo and Semver - 2021][optimize-with-cargo-and-semver] +- [Tighten rust’s belt: shrinking embedded Rust binaries - 2022][tighten-rusts-belt] +- [Avoiding allocations in Rust to shrink Wasm modules - 2022][avoiding-allocations-shrink-wasm] +- [A very small Rust binary indeed - 2022][a-very-small-rust-binary] +- [The dark side of inlining and monomorphization - 2023][dark-side-of-inlining] +- [Making Rust binaries smaller by default - 2024][making-rust-binaries-smaller-by-default] +- [Tock Binary Size - 2024][tock-binary-size] +- [Trimming down a rust binary in half - 2024][trimming-down-a-rust-binary-in-half] +- [Reducing WASM binary size: lessons from building a web terminal - 2024][reducing-wasm-binary-size] - [`min-sized-rust-windows`][min-sized-rust-windows] - Windows-specific tricks to reduce binary size +- [Shrinking `.wasm` Code Size][shrinking-wasm-code-size] + +[151-byte-static-linux-binary]: https://mainisusuallyafunction.blogspot.com/2015/01/151-byte-static-linux-binary-in-rust.html [why-rust-binary-large]: https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html + [fmt-unreasonably-expensive]: https://jamesmunns.com/blog/fmt-unreasonably-expensive/ + [tiny-windows-exe]: https://www.codeslow.com/2019/12/tiny-windows-executable-in-rust.html + [tiny-webassembly-graphics]: https://cliffle.com/blog/bare-metal-wasm/ + [gstreamer-plugin]: https://www.collabora.com/news-and-blog/blog/2020/04/28/reducing-size-rust-gstreamer-plugin/ + +[optimizing-rust-binary-size]: https://arusahni.net/blog/2020/03/optimizing-rust-binary-size.html + +[minimizing-mender-rust]: https://mender.io/blog/building-mender-rust-in-yocto-and-minimizing-the-binary-size + +[optimize-with-cargo-and-semver]: https://oknozor.github.io/blog/optimize-rust-binary-size/ + +[tighten-rusts-belt]: https://dl.acm.org/doi/abs/10.1145/3519941.3535075 + +[avoiding-allocations-shrink-wasm]: https://nickb.dev/blog/avoiding-allocations-in-rust-to-shrink-wasm-modules/ + +[a-very-small-rust-binary]: https://darkcoding.net/software/a-very-small-rust-binary-indeed/ + +[dark-side-of-inlining]: https://nickb.dev/blog/the-dark-side-of-inlining-and-monomorphization/ + +[making-rust-binaries-smaller-by-default]: https://kobzol.github.io/rust/cargo/2024/01/23/making-rust-binaries-smaller-by-default.html + +[tock-binary-size]: https://tweedegolf.nl/en/blog/126/tock-binary-size + +[trimming-down-a-rust-binary-in-half]: https://tech.dreamleaves.org/trimming-down-a-rust-binary-in-half/ + +[reducing-wasm-binary-size]: https://www.warp.dev/blog/reducing-wasm-binary-size + [min-sized-rust-windows]: https://github.com/mcountryman/min-sized-rust-windows - -[travis-build-status]: https://travis-ci.com/johnthagen/min-sized-rust -[travis-build-status-svg]: https://travis-ci.com/johnthagen/min-sized-rust.svg?branch=master +[shrinking-wasm-code-size]: https://rustwasm.github.io/docs/book/reference/code-size.html + +# Organizations + +- [wg-binary-size]: Working group for improving the size of Rust programs and libraries. + +[wg-binary-size]: https://github.com/rust-lang/wg-binary-size + +# Legacy Techniques + +The following techniques are no longer relevant for modern Rust development, but may apply to older +versions of Rust and are maintained for historical purposes. + +## Remove Jemalloc + +![Minimum Rust: 1.28](https://img.shields.io/badge/Minimum%20Rust%20Version-1.28-brightgreen.svg) +![Maximum Rust: 1.31](https://img.shields.io/badge/Maximum%20Rust%20Version-1.31-brightgreen.svg) + +> [!IMPORTANT] +> As of Rust 1.32, +> [`jemalloc` is removed by default](https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html). +> **If using Rust 1.32 or newer, no action is needed to reduce binary size regarding this +> feature**. + +**Prior to Rust 1.32**, to improve performance on some platforms Rust bundled +[jemalloc](https://github.com/jemalloc/jemalloc), an allocator that often +outperforms the default system allocator. Bundling jemalloc added around 200KB +to the resulting binary, however. + +To remove `jemalloc` on Rust 1.28 - Rust 1.31, add this code to the top of `main.rs`: + +```rust +use std::alloc::System; + +#[global_allocator] +static A: System = System; +``` diff --git a/build_std/Cargo.toml b/build_std/Cargo.toml index f80c8ce..b33bfe1 100644 --- a/build_std/Cargo.toml +++ b/build_std/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "build_std" version = "0.1.0" -edition = "2018" +edition = "2021" license = "MIT" [dependencies] diff --git a/no_main/.gitignore b/no_main/nix/.gitignore similarity index 100% rename from no_main/.gitignore rename to no_main/nix/.gitignore diff --git a/no_main/Cargo.lock b/no_main/nix/Cargo.lock similarity index 100% rename from no_main/Cargo.lock rename to no_main/nix/Cargo.lock diff --git a/no_main/Cargo.toml b/no_main/nix/Cargo.toml similarity index 77% rename from no_main/Cargo.toml rename to no_main/nix/Cargo.toml index cb3b7d6..1eb6c8b 100644 --- a/no_main/Cargo.toml +++ b/no_main/nix/Cargo.toml @@ -1,8 +1,7 @@ [package] name = "no_main" version = "0.1.0" -authors = ["johnthagen ", "Vitaly '_Vi' Shukela "] -edition = "2018" +edition = "2021" license = "MIT" [dependencies] diff --git a/no_main/src/main.rs b/no_main/nix/src/main.rs similarity index 100% rename from no_main/src/main.rs rename to no_main/nix/src/main.rs diff --git a/no_main/win/.gitignore b/no_main/win/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/no_main/win/.gitignore @@ -0,0 +1 @@ +/target diff --git a/no_main/win/Cargo.lock b/no_main/win/Cargo.lock new file mode 100644 index 0000000..e1d1daa --- /dev/null +++ b/no_main/win/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "no_main" +version = "0.1.0" diff --git a/no_main/win/Cargo.toml b/no_main/win/Cargo.toml new file mode 100644 index 0000000..46a9e1c --- /dev/null +++ b/no_main/win/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "no_main" +version = "0.1.0" +edition = "2021" +license = "MIT" + +[dependencies] + +[profile.release] +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" +strip = true diff --git a/no_main/win/src/main.rs b/no_main/win/src/main.rs new file mode 100644 index 0000000..018a9bf --- /dev/null +++ b/no_main/win/src/main.rs @@ -0,0 +1,24 @@ +#![no_main] + +use std::fs::File; +use std::io::Write as _; +use std::os::windows::{io::FromRawHandle as _, raw::HANDLE}; + +#[link(name = "kernel32")] +extern "system" { + pub fn GetStdHandle(nstdhandle: u32) -> HANDLE; +} + +pub const STD_OUTPUT_HANDLE: u32 = 4294967285; + +fn stdout() -> File { + unsafe { File::from_raw_handle(GetStdHandle(STD_OUTPUT_HANDLE)) } +} + +#[no_mangle] +pub fn main(_argc: i32, _argv: *const *const u8) -> u32 { + let mut stdout = stdout(); + stdout.write_all(b"Hello, world!\n").unwrap(); + + 0 +} diff --git a/no_std/Cargo.lock b/no_std/Cargo.lock deleted file mode 100644 index 4879888..0000000 --- a/no_std/Cargo.lock +++ /dev/null @@ -1,14 +0,0 @@ -[[package]] -name = "libc" -version = "0.2.99" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "min-sized-no_std" -version = "0.1.0" -dependencies = [ - "libc 0.2.99 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum libc 0.2.99 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" diff --git a/no_std/.gitignore b/no_std/nix/.gitignore similarity index 100% rename from no_std/.gitignore rename to no_std/nix/.gitignore diff --git a/no_std/nix/Cargo.lock b/no_std/nix/Cargo.lock new file mode 100644 index 0000000..a9ed9a7 --- /dev/null +++ b/no_std/nix/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "min-sized-no_std" +version = "0.1.0" +dependencies = [ + "libc", +] diff --git a/no_std/Cargo.toml b/no_std/nix/Cargo.toml similarity index 92% rename from no_std/Cargo.toml rename to no_std/nix/Cargo.toml index 3d6c47b..6c5d73f 100644 --- a/no_std/Cargo.toml +++ b/no_std/nix/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "min-sized-no_std" version = "0.1.0" -authors = ["johnthagen "] +edition = "2021" license = "MIT" [dependencies] diff --git a/no_std/src/main.rs b/no_std/nix/src/main.rs similarity index 100% rename from no_std/src/main.rs rename to no_std/nix/src/main.rs diff --git a/no_std/win/.gitignore b/no_std/win/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/no_std/win/.gitignore @@ -0,0 +1 @@ +/target diff --git a/no_std/win/Cargo.lock b/no_std/win/Cargo.lock new file mode 100644 index 0000000..e69bffc --- /dev/null +++ b/no_std/win/Cargo.lock @@ -0,0 +1,76 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "no_std_win" +version = "0.1.0" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" diff --git a/no_std/win/Cargo.toml b/no_std/win/Cargo.toml new file mode 100644 index 0000000..d0aac8e --- /dev/null +++ b/no_std/win/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "no_std_win" +version = "0.1.0" +edition = "2021" + +[dependencies] +windows-sys = { version = "0.52.0", features = [ + "Win32_Foundation", + "Win32_System_Threading", # for `ExitProcess` + "Win32_System_Console", # for `WriteConsoleA` etc. +] } + +[profile.dev] +panic = "abort" + +[profile.release] +opt-level = "z" # Optimize for size. +lto = true # Enable Link Time Optimization +codegen-units = 1 # Reduce number of codegen units to increase optimizations. +panic = "abort" # Abort on panic +strip = true # Automatically strip symbols from the binary. diff --git a/no_std/win/src/main.rs b/no_std/win/src/main.rs new file mode 100644 index 0000000..0c15a52 --- /dev/null +++ b/no_std/win/src/main.rs @@ -0,0 +1,36 @@ +#![no_main] +#![no_std] +#![windows_subsystem = "console"] + +use core::ffi::c_void; +use core::panic::PanicInfo; + +use windows_sys::Win32::System::Console::GetStdHandle; +use windows_sys::Win32::System::Console::WriteConsoleA; +use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; +use windows_sys::Win32::System::Threading::ExitProcess; + +#[panic_handler] +fn panic(_: &PanicInfo<'_>) -> ! { + unsafe { + ExitProcess(1); + } +} + +#[allow(non_snake_case)] +#[no_mangle] +fn mainCRTStartup() -> ! { + let message = "Hello, world!\n"; + unsafe { + let console = GetStdHandle(STD_OUTPUT_HANDLE); + WriteConsoleA( + console, + message.as_ptr().cast::(), + message.len() as u32, + core::ptr::null_mut(), + core::ptr::null(), + ); + + ExitProcess(0) + } +}