Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow asserts and panics to abort process quietly. #54981

Closed
vi opened this issue Oct 11, 2018 · 18 comments · Fixed by #55011
Closed

Allow asserts and panics to abort process quietly. #54981

vi opened this issue Oct 11, 2018 · 18 comments · Fixed by #55011
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR.

Comments

@vi
Copy link
Contributor

vi commented Oct 11, 2018

Currently assertions cause executable to be bloated up by various formatting-related symbols like

 0.5%   2.8%   1020B       std <core::fmt::builders::PadAdapter<'_> as core::fmt::Write>::write_str
 0.4%   2.3%    860B       std core::char::methods::<impl char>::escape_debug
 0.4%   2.2%    804B       std core::fmt::Formatter::pad_integral

, causing executables that carefully choose functions to call in order to stay under X kilobytes to suddenly grow by about 40-150 kilobytes. Additionally, pthread_* symbols appear in otherwise single-threaded module. Symbol count may jump by about 800.

Shall there be some mode (like panic = "really-abort") or a libstd feature settable in Xargo.toml that makes all panics just do plain raw abort without trying to collect backtrace or print anything at all?

@vi
Copy link
Contributor Author

vi commented Oct 11, 2018

Looks like making panicking::{continue_panic_fmt,begin_panic} just do the abort makes it small again.

Is a pull request about this welcome?

@Havvy Havvy added the C-feature-request Category: A feature request, i.e: not implemented / a PR. label Oct 11, 2018
vi added a commit to vi/rust that referenced this issue Nov 29, 2018
It stop asserts and panics from libstd to automatically
include string output and formatting code.

Use case: developing static executables smaller than 50 kilobytes,
where usual formatting code is excessive while keeping debuggability
in debug mode.

May resolve rust-lang#54981.
bors added a commit that referenced this issue Nov 30, 2018
Add libstd Cargo feature "panic_immediate_abort"

It stop asserts and panics from libstd to automatically
include string output and formatting code.

Use case: developing static executables smaller than 50 kilobytes,
where usual formatting code is excessive while keeping debuggability
in debug mode.

May resolve #54981.
kennytm added a commit to kennytm/rust that referenced this issue Nov 30, 2018
…chton

Add libstd Cargo feature "panic_immediate_abort"

It stop asserts and panics from libstd to automatically
include string output and formatting code.

Use case: developing static executables smaller than 50 kilobytes,
where usual formatting code is excessive while keeping debuggability
in debug mode.

May resolve rust-lang#54981.
@vi
Copy link
Contributor Author

vi commented Nov 30, 2018

With the new feature merged in, one can opt out formatting and panic handling using panic_immediate_abort and #![no_main]. This can bring static executable size under 20 kilobytes.

You need to

  • use Xargo,
  • #![no_main] #[no_mangle] pub fn main(...) {...},
  • std = {default-features=false, features=["panic_immediate_abort"]} line in Xargo.toml,
  • panic=abort,
  • LTO,
  • and obviously avoid println! or format! yourself (including in libs).

All panics and failed asserts will just cause Illegal Instruction.

@rhendric
Copy link

rhendric commented Dec 9, 2018

Hey @vi, I'm so glad you got this in! I followed your instructions above and my binaries are getting smaller. However, ldd tells me that there's still a libpthread dependency in my compiled (single-threaded) binaries. Have you succeeded in getting rid of that?

@vi
Copy link
Contributor Author

vi commented Dec 9, 2018

Try the minimal example:

src/main.rs:

#![no_main]

#[no_mangle]
pub fn main() {

}

Xargo.toml:

[dependencies]
std = {default-features=false, features=["panic_immediate_abort"]}

Cargo.toml:

[package]
name = "tiny"
version = "0.1.0"
authors = []
edition = "2018"

[profile.release]
opt-level = "s"
debug = false
rpath = false
lto = true
debug-assertions = false
codegen-units = 1
panic = 'abort'
incremental = false

Commands:

$ xargo build --release --target=x86_64-unknown-linux-gnu
    Updating crates.io index
   Compiling cc v1.0.25
   Compiling build_helper v0.1.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/build_helper)
   Compiling core v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore)
   Compiling unwind v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libunwind)
   Compiling compiler_builtins v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/rustc/compiler_builtins_shim)
   Compiling cmake v0.1.35
   Compiling std v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd)
   Compiling rustc_asan v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/librustc_asan)
   Compiling rustc_tsan v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/librustc_tsan)
   Compiling rustc_msan v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/librustc_msan)
   Compiling rustc_lsan v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/librustc_lsan)
   Compiling libc v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/rustc/libc_shim)
   Compiling alloc v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/liballoc)
   Compiling panic_abort v0.0.0 (/nix/rust/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libpanic_abort)
    Finished release [optimized] target(s) in 1m 01s
   Compiling tiny v0.1.0 (/tmp/rstiny)
    Finished release [optimized] target(s) in 0.52s
$ strip target/x86_64-unknown-linux-gnu/release/tiny -o tiny

$ ldd tiny 
	linux-vdso.so.1 (0x000003fdde54f000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000003d847699000)
	/lib64/ld-linux-x86-64.so.2 (0x0000031694ddc000)

$ ls -lh tiny 
-rwxrwxr-x 1 vi vi 6.0K Dec 10 00:21 tiny

Is it the same for you?

@rhendric
Copy link

rhendric commented Dec 9, 2018

You're right, I get the same as you. But trying that helped me figure out that adding the libc crate seems to be what adds the libpthread dependency, so thanks!

@vi
Copy link
Contributor Author

vi commented Dec 9, 2018

Strange.

#![no_main]
#[no_mangle]
pub unsafe fn main() {
    libc::_exit(0);
}

does not include pthread, but

#![no_main]
#[no_mangle]
pub unsafe fn main() {
    libc::write(1, b"Hello, world\n\0" as *const u8 as *const libc::c_void, 14);
    libc::_exit(0);
}

does...

@vi
Copy link
Contributor Author

vi commented Dec 9, 2018

This one again does not include pthread:

#![no_main]

#[no_mangle]
pub unsafe fn main() {
    libc::syscall(libc::SYS_write, 1, b"Hello, world\n\0" as *const u8 as *const libc::c_void, 14);
    libc::_exit(0);
}

@rhendric
Copy link

I got rid of pthread by depending on libc with default-features = false.

Before that, I noticed that the libc crate functions that led to libpthread being linked in (besides write, open and close seem to do it as well, but not several others like system, creat, or openat) seem to be precisely those functions that also have definitions in libpthread, so I think without default-features = false, those functions get linked to their libpthread implementations, not their single-threaded libc implementations. I don't really understand how that happens; I found a complex set of conditional linker attributes in libc which seem like they might control this, but turning off default features means that use_std becomes false, in which case it looks like the pthread dependency should become enabled instead of disabled, despite what I'm seeing. 🤷‍♂️ I'll leave that for the next wanderer to puzzle over.

@johnthagen
Copy link
Contributor

@vi In your tiny example, is there a reason you used opt-level = "s" instead of opt-level = "z"? I thought "z" was always strictly better at minimizing the size.

@vi
Copy link
Contributor Author

vi commented Dec 12, 2018

@johnthagen , I don't think s vs z is relevant for such a small example. Unlike most other measures to reduce size bloat, z can cause more performance loss when compared to s. And I don't remember observing significant size changes between s and z. Is the differene only about loop unrolling or there's more?

Also the example was investigation of when it links to libpthread and when not, so small size wasn't a focus. opt-level="s" is just inserted by default in my xargoize script.

@johnthagen
Copy link
Contributor

Is the differene only about loop unrolling or there's more?

That's the only thing I've heard of. The Cargo reference just says:

# 's' attempts to reduce size, 'z' reduces size even more.

@johnthagen
Copy link
Contributor

@vi Looks like the latest nightly (rustc 1.32.0-nightly (f4a421ee3 2018-12-13)) might have broken this example / Xargo?

https://travis-ci.org/johnthagen/min-sized-rust/jobs/467982878#L498

@vi
Copy link
Contributor Author

vi commented Dec 15, 2018

@johnthagen , Indeed: #56092

this almost for sure breaks out-of-tree std-building tools like xargo and cargo-xbuild

Fortunately, something like a fix seems to be already available: japaric/xargo#229 japaric/xargo#228.

ischeinkman pushed a commit to ischeinkman/libnx-rs-std that referenced this issue Dec 20, 2018
It stop asserts and panics from libstd to automatically
include string output and formatting code.

Use case: developing static executables smaller than 50 kilobytes,
where usual formatting code is excessive while keeping debuggability
in debug mode.

May resolve rust-lang#54981.
@johnthagen
Copy link
Contributor

@vi Do you know if panic_immediate_abort can be enabled using -Z build-std rather than Xargo? Given Xargo has been in maintenance mode for years, it seems like build-std would be preferable. If so, would you be able to provide an example on how to enable it in a build-std build?

@johnthagen
Copy link
Contributor

After playing around a bit with this myself, I was able to get this to work by using -Z build-std-features:

cargo +nightly build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu --release;

@leighmcculloch
Copy link

Is there an issue tracking stabilizing panic_immediate_abort?

@F8RZD
Copy link

F8RZD commented Mar 29, 2024

Is there an issue tracking stabilizing panic_immediate_abort?

I'm also really interested in this but didn't find any just by searching where can we read about progress on that or even if it's planned to stable

@ChrisDenton
Copy link
Member

It requires rebuilding the standard library, which means it depends on build-std.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-feature-request Category: A feature request, i.e: not implemented / a PR.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants