Minimal. Executable. Rusticle.
Rusticle is a byte-sized Windows executable written in pure Rust, designed to explore how small a functional .exe can be—without sacrificing clarity, correctness, or control. This project began with a standard cargo new application that printed "Hello, world!", producing a 134,144-byte binary on Windows (Rust 1.88.0). Through a series of deliberate, incremental changes, we reduced that footprint dramatically—without introducing any dependencies or compromising readability.
Each minor version introduced in this repository represents a single optimization step, accompanied by a clear explanation. From tuning compiler flags to removing the VC runtime, from stripping symbols to redefining the entry point, every decision was made with precision and purpose.
- ✅ Use the default Rust toolchain and linker
- ✅ Keep the code readable and maintainable
- ✅ Avoid all external dependencies
- ✅ Introduce features only when implemented
- ✅ Measure and document every change
This table tracks the evolution of Rusticle across versions, showing how each change impacted the final .exe size on both x86_64 and i686 architectures.
| Version | Change Summary | x86_64 Size (bytes) |
i686 Size (bytes) |
|---|---|---|---|
| v1.0.0 | Initial cargo new project with println! |
138,240 | 117,760 |
| v1.1.0 | Built in release mode (cargo build --release) |
134,144 | 116,224 |
| v1.2.0 | Enabled LTO for link-time optimization | 122,368 | 105,472 |
| v1.3.0 | Switched panic strategy from unwind to abort | 119,296 | 103,424 |
| v1.4.0 | Switched to opt-level = "z" for size optimization | 115,200 | 99,328 |
| v1.5.0 | Replaced main() with mainCRTStartup | 105,984 | 91,136 |
| v1.6.0 | Switched to no_std w/ Win32 API for print + exit | 3,072 | 3,072 |
| v1.7.0 | Set /ALIGN:8 to minimize alignment overhead. |
1,296 | 1,176 |
| v1.8.0 | Disabled debug info by setting DEBUG:NONE. |
1,176 | 1,064 |
| v1.9.0 | Merged .pdata into .text section |
1,128 | 1,064 |
| v1.10.0 | Added custom 64-byte dummy DOS stub | 1,064 | 1,000 |
| v1.11.0 | Disabled Rich Header w/ /EMITTOOLVERSIONINFO:NO |
1,008 | 944 |
| v1.12.0 | Added /EMITPOGOPHASEINFO - Removes PGO metadata |
712 | 680 |
| v1.13.0 | Switched from WriteConsoleA to WriteFile |
704 | 672 |
| v1.14.0 | Replaced GetStdHandle with pseudo-handle |
664 | 640 |
| v1.15.0 | Enforced fixed base address; disabled ASLR1 | 664 | 584 |
| v1.16.0 | lowered /ALIGN to 4 bytes on 32-bit builds | 664 | 580 |
| v1.17.0 | Pass NULL pointer for lpNumberOfBytesWritten | 656 | 576 |
| v1.18.0 | Specifying #[link(name = "kernel32")] explicitly | 648 | 564 |
| v1.19.0 | Return directly from entrypoint (mainCRTStartup) | 616 | 540 |
| v1.20.0 | Embed output message in .text section of binary | 616 | 536 |
📌 All builds use the default Rust toolchain and linker. No external crates or dependencies are introduced at any stage.
- How to build a minimal Windows executable in Rust
- How to progressively reduce binary size without sacrificing clarity
- How to remove runtime dependencies like the VC runtime
- How to control the PE layout and entry point manually
- How to write Rust that feels like shellcode—but compiles like a dream
The final binary is a fraction of its original size, with no external dependencies, no runtime, and no standard library. It executes cleanly, exits predictably, and leaves behind nothing but admiration for what Rust can do when stripped to its essence.
This project is unlicensed. You are free to use, modify, distribute, or embed it however you like—no restrictions, attribution welcome but not required.
Pull requests are welcome. Whether you're optimizing further, experimenting with new techniques, or just curious about byte-level Rust, feel free to contribute.
Footnotes
-
Thanks to /u/Mognakor/ on Reddit for making me reconsider the /FIXED flag. ↩