sgx_panic_backtrace/
lib.rs

1//! # sgx-panic-backtrace
2//!
3//! A small library for printing out panics and backtraces inside an SGX enclave.
4//!
5//! ## Why
6//!
7//! + Get backtraces working while we wait for `backtrace-rs` to get fixed : )
8//!
9//! ## Usage
10//!
11//! Add `sgx-panic-backtrace` to your `Cargo.toml`:
12//!
13//! ```toml
14//! [dependencies]
15//! sgx-panic-backtrace = "0.1.0"
16//! ```
17//!
18//!
19//! In the enclave, call `sgx_panic_backtrace::set_panic_hook()` in your main
20//! function:
21//!
22//! ```rust,no_run
23//! sgx_panic_backtrace::set_panic_hook();
24//! ```
25//!
26//! If the enclave panics (and panic=abort is not turned on!) it will now print
27//! out the raw backtrace frames to stdout. These include only the frame index
28//! and relative frame instruction pointer offset, which you'll need to symbolize
29//! outside the enclave itself.
30//!
31//! ```bash
32//! $ cargo run --target=x86_64-fortanix-unknown-sgx
33//!
34//! enclave: panicked at 'foo', bar.rs:10:5
35//! stack backtrace:
36//!    0: 0x1b09d9
37//!    1: 0x1396f6
38//!    2: 0x10f4cc
39//!    3: 0x48b3ef
40//!    4: 0x2d540b
41//!    5: 0x2d56fa
42//!    6: 0x2d531d
43//!    7: 0x16c681
44//!    8: 0x116fd0
45//!    9: 0x13410e
46//! ```
47//!
48//! To get human readable symbol names and locations from these raw ips, you may
49//! wish to use the `stack-trace-resolve` utility that comes with the Fortanix
50//! EDP.
51//!
52//! For example:
53//!
54//! ```bash
55//! $ ftxsgx-runner <my-enclave-bin>.sgxs | stack-trace-resolve <my-enclave-bin>
56//! ```
57
58use std::{io::Write, panic};
59
60/// Return the base address of the currently loaded SGX enclave binary. Vendoring
61/// this lets us avoid requiring the unstable `sgx_platform` feature.
62///
63/// This is copied from: [std::os::fortanix_sgx::mem::image_base](https://github.com/rust-lang/rust/blob/master/library/std/src/sys/sgx/abi/mem.rs#L37)
64// NOTE: Do not remove inline: will result in relocation failure.
65#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
66#[inline(always)]
67fn image_base() -> u64 {
68    use std::arch::asm;
69
70    let base: u64;
71    unsafe {
72        asm!(
73            // `IMAGE_BASE` is defined here:
74            // [std/src/sys/sgx/abi/entry.S](https://github.com/rust-lang/rust/blob/master/library/std/src/sys/sgx/abi/entry.S#L5)
75            "lea IMAGE_BASE(%rip), {}",
76            lateout(reg) base,
77            options(att_syntax, nostack, preserves_flags, nomem, pure),
78        )
79    };
80    base
81}
82
83#[cfg(not(all(target_vendor = "fortanix", target_env = "sgx")))]
84fn image_base() -> u64 {
85    0
86}
87
88/// Trace each frame and print each relative instruction pointer offset. These
89/// offsets should be symbolized these outside the enclave.
90fn print_backtrace_frames() {
91    println!("stack backtrace:");
92
93    let mut frame_idx: usize = 0;
94    unsafe {
95        backtrace::trace_unsynchronized(|frame| {
96            let base_addr = image_base() as usize;
97
98            // we need the ip offsets relative to the binary base address.
99            let ip = (frame.ip() as usize).saturating_sub(base_addr);
100
101            println!("{frame_idx:>4}: {ip:#x}");
102            frame_idx += 1;
103
104            // TODO(phlip9): be smarter and ignore frames inside the
105            // panic/backtrace code.
106            // keep tracing until we run out of frames
107            true
108        })
109    }
110    println!();
111}
112
113/// Set a panic hook that will print out the panic and raw backtrace addresses
114/// when the enclave panics. These addresses will need to be symbolized to human-
115/// readable symbol names and locations outside the enclave with a tool like
116/// `addr2line`.
117pub fn set_panic_hook() {
118    let prev_hook = panic::take_hook();
119    panic::set_hook(Box::new(move |panic_info| {
120        // The default panic hook also doesn't print out the panic message, so
121        // let's do that here.
122        println!("enclave panic: {panic_info}");
123
124        // trace the stack frames and print them out
125        print_backtrace_frames();
126
127        // enclave's about to abort. let's try to flush stdout so we get the
128        // full panic message out. ignore any errors so we don't double panic.
129        let _ = std::io::stdout().flush();
130
131        // continue the default panic behaviour.
132        prev_hook(panic_info);
133    }));
134}