Coder Social home page Coder Social logo

min-sized-rust's Introduction

GitHub stats

Top Langs

min-sized-rust's People

Contributors

drmikehenry avatar edmorley avatar frago9876543210 avatar johnthagen avatar stusmall avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

min-sized-rust's Issues

Mention stripping additional sections to slightly reduce binary size

It seems that there are some sections in the binary produced by rustc that can be stripped without (?) impacting the program behavior. I did some testing on the x86_64-unknown-linux-musl target with #![no_main], and I successfully stripped the following sections on the "Hello world" binary:

.comment
.init_array
.dynsym
.gnu.hash
.fini_array
.eh_frame
.eh_frame_hdr
.dynstr
.plt
.plt.got
.tdata
.got
.tbss

Some of these sections are probably necessary for some binaries, or they may subtly corrupt the program when stripped, so further research should be done. At the very least, I think that the .comment and .gnu.hash sections can be freely stripped, although this will only shave off a couple bytes. Stripping .gnu.hash leads to UPX refusing to pack the executable, though.

Explain better for speed at runtime

I read both:

What I don't understand is which speed gain I'm changing if I use:

[profile.release]
opt-level = "z"

instead of:

[profile.release]
opt-level = 3
  • Is it right that today opt-level = 3 is the best setting (for opt-level section) for runtime speed?

  • If I instead use opt-level = "z" I'm decreasing runtime performance, right?

I'm not interested in building/compiling speed.

1.45.0 is now stable :)

Thanks for this great repo!

Just wanted to point out that in the strip Symbols from Binary section, it mentions nightly 1.45.0, however, this is now a stable version.

Local file path strings embedded in binary

Hello!

I'm working through this awesome guide to reduce the size of my compiled binaries. It is making a big difference. While inspecting the result, I used strings to take a look at what strings are still in the result. I see multiple places where it appears that the resultant release binary contains file paths which are a reflection of my local file system. Also, these paths are sometimes long and thus also contributing slightly to binary size. I'd like to understand why these full paths are being included and if there is a way to suppress them.

Your help, thoughts, and ideas are much appreciated!

Here is my release compile profile:

[profile.release]
# Cf. https://github.com/johnthagen/min-sized-rust
strip = true # automatically strip symbols from the binary
opt-level = "s" # optimize for binary size
lto = true # link-time optimization enabled
codegen-units = 1 # prevent build-time parallelization
debug = false
panic = "abort"

And, here is a redacted sample of the output of strings on the resultant binary:

release/macos/aarch64/bin/foo | grep "ptdecker"

Output (redacted):

[redacted]
/Users/ptdecker/.cargo/registry/src/index.crates.io-6f17d22bba15001f/indexmap-2.1.0/src/map/core.rsBigNumber failed to deserialize: 11Broadcast is less than 4 bytes and does not include key version Keys were generated with Crypto Module Version ./Users/ptdecker/PycharmProjects/foo/fizz/src/common/secure_big_num.rsSecureBigNumber failed to deserialize:
gcd(challenge,N) != 1 for Paillier GCD Proof Challenge Generation!/Users/ptdecker/PycharmProjects/bar/[redacted]
/rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/sync/atomic.rsthere is no such thing as an acquire storethere is no such thing as an acquire-release storeinternal error: entered unreachable code: unknown state: /Users/ptdecker/.cargo/registry/src/index.crates.io-6f17d22bba15001f/want-0.3.1/src/lib.rs/Users/ptdecker/.cargo/registry/src/index.crates.io-6f17d22bba15001f/zeroize-1.6.0/src/lib.rsassertion failed: size <= isize::MAX as usize
/Users/ptdecker/PycharmProjects/foo/fizz/target/aarch64-apple-darwin/release/build/openssl-sys-f6a0d3a636f0f1e4/out/openssl-build/install/lib/engines-3
/Users/ptdecker/PycharmProjects/foo/fizz/target/aarch64-apple-darwin/release/build/openssl-sys-f6a0d3a636f0f1e4/out/openssl-build/install/lib/ossl-modules
[redacted]

Note the /Users/ptdecker/PycharmProjects/baz/foo/target... paths

Ideas to enhance the project

  1. Use (or add) less trivial example, with some external dependencies. For example, opening a file, calculating md5summ, outputting it in hex.
  2. Include not just target size for MacOS, but a table of sizes across major platforms.

Add note about cross compiling via nightly

It is possible to cross compile w/ the +nightly targets.

ex.

rustup +nightly target add x86_64-unknown-linux-musl
cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort \
    --target x86_64-unknown-linux-musl --release

Will work, this can be added as an addendum to the Optimize libstd and Remove Panic String sections.

Discovered from: rust-lang/wg-cargo-std-aware#76

`-C target-cpu=native`

By using RUSTFLAGS="-C target-cpu=native", the resulting binary will be far less portable, but it can reduce the binary size a fair amount.

Strip is now default

Starting from Rust 1.77, Rust strips dbg info by default in release mode (excluding MacOS). Given that strip is the second tip after using release, it's probably worthwhile to update the information. I can write. a PR tomorrow when back at a computer:

rust-lang/cargo#13257

Is there no way to use --gc-sections in rust?

That linker option along with ffunction-sections makes a massive difference at least with haskell (where I think the flag is split-sections). Or further lld's identical code folding. I was hoping to learn about these here

Add a reference to "A very small Rust binary indeed"

https://darkcoding.net/software/a-very-small-rust-binary-indeed/

This blog post explains the same techniques used in this repo to shrink the binary size, but goes one step further to remove dependency on libc and use syscalls directly. I found it really useful when creating a no_std binary without libc.

The blog post also points out some other ideas, which can be applied to any Rust project:

  • Disabling PIE (can be done with relocation-model=static)
  • Telling the linker not to align sections (-n and -N flags)
  • Removing BuildID from ELF files with --build-id=none

The gains from this probably only matter for really small binaries (below 10kb), so I think it's reasonable to just add a reference to the blog post in the footer.

-Zlocation-detail=none

There's a nightly flag rust-lang/rust#89920 that removes file/line/column information from panics and [track_caller]. Apart from reduction in the location data, this sometimes also lets the compiler unify code paths of multiple panics into one.

Identical code folding

Since Rust can't do polymorphization properly yet, using generics generates a lot of duplicated functions because of monomorphization. These functions take space in the binary, even though they have completely the same instructions.

Some linkers (gold, lld) can deduplicate these identical functions using Identical Code Folding, and thus reduce binary size (and potentially also improve usage of the i-cache).

You can specify this linker option using a linker flag, for example like this:

$ RUSTFLAGS="-Clink-args=-fuse-ld=lld -Clink-args=-Wl,--icf=all" cargo build

I measured the binary size change for the following program:

fn foo() {
    let mut a: Vec<u8> = vec![1, 2, 3];
    a.pop();

    let mut b: Vec<u32> = vec![1, 2, 3];
    b.pop();

    let mut c: Vec<i32> = vec![1, 2, 3];
    c.pop();
}

fn main() {
    foo();
}

Here are binary sizes are after running strip on them:

Linker Mode Binary size (B) ICF (Identical Code Folding)
gold debug 342696 No
gold debug 330408 Yes
gold release 322216 No
gold release 318120 Yes
lld debug 330968 No
lld debug 321840 Yes
lld release 310616 No
lld release 306848 Yes

Add note about monomorphisation

Generics with static dispatch can lead to duplicated functions, and if they are big enough, it will make for a large piece of the final binary.

Alternatives are dynamic dispatch with dyn, or just removing generic code.

Is seems very important for minimizing binary size.

(If you don't want to change the function API, and the function is being generic over something like AsRef<Path>, you can also create helper function which resolves path.as_ref() and calls a non-generic function that receives &Path instead.)

Unable to strip information

Running $ cargo +nightly build -Z strip=symbols on Windows I get this error message:

error: unknown `-Z` flag specified: strip

I just updated my nightly toolchain still the same error. Any idea what this may be happening?

min sized -lib (.a)

I followed the instructions to compile the library, creating a static library(.a) that contains only the simplest method. The final size is 221kb. Is this in line with expectations?

[package]
name = "greetings"
version = "0.1.1"
authors = ["fluffyemily <[email protected]>"]
description = "Example static library project built for iOS"
publish = false

[target.'cfg(target_os="android")'.dependencies]
jni = { version = "0.5", default-features = false }

[lib]
name = "greetings"
crate-type = ["staticlib", "cdylib"]

[profile.release]
debug = false
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.

Add license information

Hello!

Could you please add information about license of this repository โ€” is it allowed to copy, change and use the code examples and documentation in any purpose?
It would be great to see any popular open source license.

Alternative strip: llvm-strip via cargo-binutils

Consider adding cargo-binutils to the README. It provides easy to use cargo commands for a few llvm tools that can be installed via rustup. These tools work for quite a few platforms including windows and is easy to get setup if you use rustup for your rust toolchain.

$ rustup component add llvm-tools-preview
$ cargo install cargo-binutils
$ cargo strip

The cargo-binutils cargo commands automatically handle building the project if needed as well as finding and passing the target binary path to the tool. They also support the majority of the command line options that cargo build does to make the tools as intuitive as using cargo itself.

Also provides access to llvm-size via cargo size.

https://github.com/rust-embedded/cargo-binutils

Disclaimer: I recently did a little bit of work on cargo-binutils to make it easier to use and more closely match the built in cargo commands, and the README is a little out of date because of that.

Resulting executable sizes are not reported consistently

On macOS, the final binary size is reduced to 51KB.

On macOS, the final binary size is reduced to 30KB.

I think each section (including "baseline, before optimisation") should have a demo project with target file size available. The size may even be in section title.

Add section with alloc only but not libstd

When rust-lang/rfcs#2480 is stabilized and implemented, it should be possible to create binaries that don't depend on std but do depend on alloc. This would be a step between no_std and no_main, because at least you could use the heap. The actual sample hello world code would probably not change much. It would probably be best to show off using a Vec in this instance.

This could probably be added now as a nightly subproject.

Mention sstrip

For the sake of completeness, it'd probably be a good idea to mention the sstrip utility from ELFkickers.

As this mirror explains:

Most ELF executables are built with both a program header table and a section header table. However, only the former is required in order for the OS to load, link and execute a program. sstrip attempts to extract the ELF header, the program header table, and its contents, leaving everything else in the bit bucket. It can only remove parts of the file that occur at the end, after the parts to be saved. However, this almost always includes the section header table, along with a few other sections that are not involved in program loading and execution.

It should be noted that most programs that work with ELF files are dependent on the section header table as an index to the file's contents. Thus, utilities such as gdb and objdump will often have limited functionality when working with an executable with no section header table. Some other utilities may refuse to work with them at all.

Basically, it's a tool primarily used for things like saving flash memory on embedded Linux devices which will shave a few more bytes off what the regular strip utility produces.

Migrate away from Travis CI

Since Travis CI is no longer free for FOSS projects, need to migrate this CI to something else, likely GitHub Actions.

Custom ld script

I created a custom ld script that makes elf64 binary smaller.
It is possible to add this script to build sequence by creating build.rs file:

fn main() {
	println!("cargo:rustc-link-arg=-Wl,-T,elf64-min.ld");
}

Here is elf64-min.ld:

OUTPUT_FORMAT("elf64-x86-64")
ENTRY(_start)
PHDRS {
	text PT_LOAD FILEHDR PHDRS;
	/*tls PT_TLS;*/ /* uncomment this if you use std or pthreads */
	interp PT_INTERP;
	dynamic PT_DYNAMIC;
}

SECTIONS
{
	. = SIZEOF_HEADERS;
	t : {
		KEEP (*(.init))
		*(.text*)
		KEEP (*(.fini))
		*(.rodata*)
		*(.data*)
		*(.bss*)
	} :text
	.init_array : {
		PROVIDE_HIDDEN (__init_array_start = .);
		KEEP (*(SORT_BY_INIT_PRIORITY(.init_array*)))
		PROVIDE_HIDDEN (__init_array_end = .);
	} :text
	.rel : { *(.data.rel*) } :text

	td : { *(.tdata*) } :text :tls
	tb : { *(.tbss*) } :text :tls

	i : {
		*(.interp)
	} :text :interp

	d : {
		*(.dynamic)
		*(.got)
		*(.got.*)
		*(.gnu.*)
		*(.dynsym)
	} :dynamic :text

	/DISCARD/ : { *(.note*) }
}

cargo-strip

Thanks a lot for this repository. It contains a lot of useful information. I found that stripping cargo generated binaries is a bit painful as you need to identify their names manually.

To simplify the process, I released cargo-strip over the week-end that automates this. It could be improved but works fine for my needs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.