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}