Repository: rust
Issue: stdlib: Missing backtrace on panic when using `std::backtrace::Backtrace` vs `backtrace::Backtrace` on Android
Labels: O-android,C-bug,requires-custom-config,S-needs-repro

When compiling standalone binaries for Android with debug information, there seems to be no backtrace information available. However it is possible to get a backtrace when using `backtrace` as external dependency.

I am building native binaries for Android devices. I noticed that I could not get a backtrace on `panic`. This is reproducible for me on a physical Samsung Galaxy Tab A9 (`aarch64-linux-android`, with Android 13 and Android API 33), on a Pixel 3a emulator (`x86_64-linux-android` with Android 14 and Android API 34)  as well as on another target device for which I cannot disclose version information.

For the rest of the report, I will only provide the instructions for the Samsung Galaxy Tab A9, but the same behavior happened with the emulator and the other Android device.

### Meta

```
$ rustc --version --verbose
rustc 1.75.0 (82e1608df 2023-12-21)
binary: rustc
commit-hash: 82e1608dfa6e0b5569232559e3d385fea5a93112
commit-date: 2023-12-21
host: x86_64-unknown-linux-gnu
release: 1.75.0
LLVM version: 17.0.6
```

## Setup

Here is a sample of code for a test binary called `stack_trace`:

```rust
fn outer_func0() {
    inner_func(false);
}

fn outer_func1() {
    inner_func(true);
}

fn inner_func(param: bool) {
    if param {
        panic!("Intentional panic");
    }
}

fn main() {
    outer_func0();
    outer_func1();
}
```

For linking, I tried two versions.
- Using `clang` as part of the latest Android NDK.
- Using the linker coming with [`cross`](https://github.com/cross-rs/cross) (apparently `-C linker=aarch64-linux-android-gcc`)

```toml
# .cargo/config.toml
# For the Galaxy Tab A9
[target.aarch64-linux-android]
linker = "/home/user/android-ndk-r26c/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang"
```

## Building

```bash
# With plan cargo
cargo build --target aarch64-linux-android

# With cross
cross build --target aarch64-linux-android

```

## Issue

When running `RUST_BACKTRACE=1 ./stack_trace`, I would expect to see the backtrace of the panic, telling me which call chain led to the panic.

Instead, I see this output (backtrace is missing):

```
$ RUST_BACKTRACE=1 ./stack_trace                                               
thread 'main' panicked at src/main.rs:11:9:
Intentional panic
stack backtrace:
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

## Analysis

You may wonder if the debug symbols got stripped, but they were not:

```bash
$ file stack_trace                                                         
stack_trace: ELF shared object, 64-bit LSB arm64, dynamic (/system/bin/linker64), for Android 33, built by NDK r26c (11394342), not stripped
```

So after some searching, I stumbled over [this comment](https://github.com/rust-lang/backtrace-rs/issues/442#issuecomment-1521546795) by @zarik5 so I tried using `backtrace` directly, setting a custom panic hook:

```rust
fn panic_with_backtrace_rs() {
    std::panic::set_hook(Box::new(|panic_info| {
        print!(
            "thread '{}' panicked",
            std::thread::current().name().unwrap_or("unknown")
        );

        if let Some(location) = panic_info.location() {
            println!(" at {}:{}:", location.file(), location.line());
        } else {
            println!("");
        }

        if let Some(payload) = panic_info.payload().downcast_ref::<&str>() {
            println!("{}", payload);
        }

        if let Ok("1") = std::env::var("RUST_BACKTRACE").as_deref() {
            println!("{:?}", backtrace::Backtrace::new());
        }
    }));
}
```

If I call this function in `main` and build with `cargo` (linking with `clang`), I get this output:

```
$ RUST_BACKTRACE=1 ./stack_trace                                           
thread 'main' panicked at src/main.rs:11:
Intentional panic
   0: <unknown>
   1: <unknown>
   2: <unknown>
   3: <unknown>
   4: <unknown>
   5: <unknown>
   6: <unknown>
   7: <unknown>
   8: <unknown>
   9: <unknown>
  10: <unknown>
  11: <unknown>
  12: <unknown>
  13: <unknown>
  14: <unknown>
  15: <unknown>

```

And if I keep the custom hook **and** build with `cross` (linking with `gcc`), I finally get a usable backtrace:

<details>

  ```
  $ RUST_BACKTRACE=1 ./stack_trace                                           
  thread 'main' panicked at src/main.rs:11:
  Intentional panic
     0: stack_trace::panic_with_backtrace_rs::{{closure}}
               at /home/user/workspace/examples/stack_trace/src/main.rs:40:30
     1: <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/alloc/src/boxed.rs:2021:9
        std::panicking::rust_panic_with_hook
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:783:13
     2: std::panicking::begin_panic_handler::{{closure}}
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:649:13
     3: std::sys_common::backtrace::__rust_end_short_backtrace
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:170:18
     4: rust_begin_unwind
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:645:5
     5: core::panicking::panic_fmt
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/panicking.rs:72:14
     6: stack_trace::inner_func
               at /home/user/workspace/examples/stack_trace/src/main.rs:11:9
     7: stack_trace::outer_func1
               at /home/user/workspace/examples/stack_trace/src/main.rs:6:5
     8: stack_trace::main
               at /home/user/workspace/examples/stack_trace/src/main.rs:19:5
     9: core::ops::function::FnOnce::call_once
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5
    10: std::sys_common::backtrace::__rust_begin_short_backtrace
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:154:18
    11: std::rt::lang_start::{{closure}}
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/rt.rs:167:18
    12: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:284:13
        std::panicking::try::do_call
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:552:40
        std::panicking::try
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:516:19
        std::panic::catch_unwind
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panic.rs:142:14
        std::rt::lang_start_internal::{{closure}}
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/rt.rs:148:48
        std::panicking::try::do_call
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:552:40
        std::panicking::try
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:516:19
        std::panic::catch_unwind
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panic.rs:142:14
        std::rt::lang_start_internal
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/rt.rs:148:20
    13: std::rt::lang_start
               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/rt.rs:166:17
    14: main
    15: __libc_init
  ```
</details>

Now you may assume it has something to do with `cross`. But on the third Android device which I tested (for which I cannot disclose further info), we also use `clang` for linking with a specific NDK version. There, I also get the "unknown" stack trace when using the default panic hook and a proper one when using `backtrace` as external dependency - however I do not need to compile with `cross`, it also works with `cargo`.

So unfortunately, I need to include `cross` in the example here to reproduce it, but I have reason to assume that this is not related to `cross` and the issue happens the same when linking with `clang`. Without a physical device, I was also able to reproduce it in the Android emulator (came as part of Android Studio). Given that I am apparently not the first person missing backtraces on Android I wonder, is a bug in the stdlib's usage of `backtrace` for Android?