Skip to content

backtrace brings in std even with std feature disabled  #564

Open

Description

@rtfeldman

I reproduced this with a minimal #![no_std] executable that does nothing but return an exit code:

main.rs

#![no_std]
#![no_main]

#[no_mangle]
fn main() -> i32 {
    42
}

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

cargo run works as expected (exits with code 42) with the following Cargo.toml, which includes the backtrace dependency (with default-features = false so it doesn't have the std feature):

Cargo.toml

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[dependencies]
backtrace = { version = "0.3.69", default-features = false }

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

At least in macOS, I needed to add compiler flags for this to run; here is my .cargo/config.toml

.cargo/config.toml

[target.'cfg(target_os = "macos")']
rustflags = [
  "-C",
  "link-arg=-e", # -e _main sets the entrypoint symbol since we're #[no_main]
  "-C",
  "link-arg=_main",
  "-C",
  "link-arg=-lSystem", # all macOS apps need -lSystem
  "-C", # Note: force-unwind-tables is not needed to reproduce
  "force-unwind-tables",
]

At this point, if I actually try to use backtrace - doing nothing else but adding use to main.rs like so:

use backtrace::trace_unsynchronized;

...now cargo check fails, saying that backtrace is bringing in the std crate which introduces a conflicting panic_impl implementation:

warning: unused import: `backtrace::trace_unsynchronized`
 --> src/main.rs:4:5
  |
4 | use backtrace::trace_unsynchronized;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

error[E0152]: found duplicate lang item `panic_impl`
  --> src/main.rs:12:1
   |
12 | fn panic(_info: &core::panic::PanicInfo) -> ! {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: the lang item is first defined in crate `std` (which `backtrace` depends on)
   = note: first definition in `std` loaded from /Users/rtfeldman/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib/libstd-1990072ee01a7130.rlib
   = note: second definition in the local crate (`example`)

For more information about this error, try `rustc --explain E0152`.

Note that the error[E0152]: found duplicate lang item 'panic_impl' explicitly says the lang item is first defined in crate 'std' (which 'backtrace' depends on), and that everything worked until I added the (unused) use backtrace::trace_unsynchronized; declaration. Also, I know the std feature is off, because if I try to import something that needs it (e.g. backtrace::Backtrace), I get an error.

This is with stable rustc 1.68.0 (2c8cc3432 2023-03-06) and cargo 1.68.0 (115f34552 2023-02-26).

Unfortunately, this seems to make it impossible to use backtrace in a no_std executable - unless I'm missing something!

Activity

  1. bjorn3 commented on Sep 6, 2023

    @bjorn3
    Member

    It looks like the gimli based symbilizer is unconditionally used on unix, even if the std feature is disabled. It requires fs libc calls to load debug info. If you are on neither Windows nor Unix you will get a noop symbolizer impl though which doesn't need libstd.

  2. rtfeldman commented on Sep 6, 2023

    @rtfeldman
    Author

    Hm, are you sure? Here's what I see for cargo tree on backtrace-rs:

    backtrace v0.3.69
    ├── addr2line v0.21.0
    │   └── gimli v0.28.0
    ├── cfg-if v1.0.0
    ├── libc v0.2.147
    ├── miniz_oxide v0.7.1
    │   └── adler v1.0.2
    ├── object v0.32.1
    │   └── memchr v2.6.3
    └── rustc-demangle v0.1.23
    [build-dependencies]
    └── cc v1.0.83
        └── libc v0.2.147
    [dev-dependencies]
    ├── dylib-dep v0.1.0
    └── libloading v0.7.4
        └── cfg-if v1.0.0
    

    So gimli v0.28.0 comes in from addr2line v0.21.0.

    addr2line 0.21.0 depends on gimli like this:

    gimli = { version = "0.28.0", default-features = false, features = ["read"] }
    

    So only the read feature. But gimli 0.28.0 only brings in the std crate when the std or write features are enabled, and addr2line doesn't enable either of those features.

    Putting those together, it seems like this indirect gimli dependency should still be no_std, yeah?

  3. bjorn3 commented on Sep 6, 2023

    @bjorn3
    Member

    The glue code in backtrace-rs which uses gimli/addr2line uses libstd:

    use mystd::ffi::OsString;
    use mystd::fs::File;
    use mystd::path::Path;
    use mystd::prelude::v1::*;
    #[cfg(backtrace_in_libstd)]
    mod mystd {
    pub use crate::*;
    }
    #[cfg(not(backtrace_in_libstd))]
    extern crate std as mystd;

  4. rtfeldman commented on Sep 7, 2023

    @rtfeldman
    Author

    Ah, I see. So I guess it's not a goal for the crate to actually support no_std, but rather that's an implementation detail of it being a part of std?

  5. workingjubilee commented on Sep 9, 2023

    @workingjubilee
    Member

    I'm happy to accept PRs that improve the situation without breaking things for std-using programs, but it is very true it might be... challenging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions