about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-01-12 10:55:07 +0100
committerGitHub <noreply@github.com>2019-01-12 10:55:07 +0100
commit85a7fc8c48f4355e2b9f86dff09e743ab5fbf92f (patch)
treec3e43edec8783c0b78a02dee094ddd010732b07e /src/libstd/sys
parent632d8909a0dbf1c8f2569987b24d36dc013ed5e8 (diff)
parent2e4766c3afcdac9f45edbd73723384b360ee4b68 (diff)
downloadrust-85a7fc8c48f4355e2b9f86dff09e743ab5fbf92f.tar.gz
rust-85a7fc8c48f4355e2b9f86dff09e743ab5fbf92f.zip
Rollup merge of #57441 - VardhanThigle:Vardhan/x86_64-fortanix-unknown-sgx-backtrace-support, r=alexcrichton
Supporting backtrace for x86_64-fortanix-unknown-sgx.

# Overview
Implementing following functions required by `libstd/sys_common` to support `backtrace`:
```
1. unwind_backtrace
2. trace_fn
3. resolve_symname
```
# Description:
The changes here are quite similar to the Cloudabi target `src/libstd/sys/cloudabi/backtrace.rs`
The first 2 functions are implemented via calls to libunwind.a that is linked to the `x86_64-fortanix-unknown-sgx` (#56979),  we have not implemented functionality needed by `resolve_symname`  (or `dladdr`) to reduce SGX TCB. Rather, we print the function address (relative to enclave image base) in `resolve_symname` which can be later translated to correct symbol name (say, via `addr2line`).

# Note:
For `x86_64-fortanix-unknown-sgx`, the `RUST_BACKTRACE` environment has to be set from within the program running in an enclave.
cc: @jethrogb
r? @alexcrichton
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/sgx/abi/mem.rs4
-rw-r--r--src/libstd/sys/sgx/backtrace.rs87
2 files changed, 81 insertions, 10 deletions
diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs
index 09552d5b4af..808f1ce3ff2 100644
--- a/src/libstd/sys/sgx/abi/mem.rs
+++ b/src/libstd/sys/sgx/abi/mem.rs
@@ -17,8 +17,10 @@ extern {
 // Do not remove inline: will result in relocation failure
 // For the same reason we use inline ASM here instead of an extern static to
 // locate the base
+/// Returns address at which current enclave is loaded.
 #[inline(always)]
-fn image_base() -> u64 {
+#[unstable(feature = "sgx_platform", issue = "56975")]
+pub fn image_base() -> u64 {
     let base;
     unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) };
     base
diff --git a/src/libstd/sys/sgx/backtrace.rs b/src/libstd/sys/sgx/backtrace.rs
index 52d4a63bb63..2b8e1da0579 100644
--- a/src/libstd/sys/sgx/backtrace.rs
+++ b/src/libstd/sys/sgx/backtrace.rs
@@ -1,21 +1,90 @@
 use io;
-use sys::unsupported;
+use error::Error;
+use libc;
 use sys_common::backtrace::Frame;
+use unwind as uw;
+use sys::sgx::abi::mem::image_base;
 
 pub struct BacktraceContext;
 
-pub fn unwind_backtrace(_frames: &mut [Frame])
-    -> io::Result<(usize, BacktraceContext)>
-{
-    unsupported()
+struct Context<'a> {
+    idx: usize,
+    frames: &'a mut [Frame],
+}
+
+#[derive(Debug)]
+struct UnwindError(uw::_Unwind_Reason_Code);
+
+impl Error for UnwindError {
+    fn description(&self) -> &'static str {
+        "unexpected return value while unwinding"
+    }
+}
+
+impl ::fmt::Display for UnwindError {
+    fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
+        write!(f, "{}: {:?}", self.description(), self.0)
+    }
+}
+
+#[inline(never)] // this function call can be skipped it when tracing.
+pub fn unwind_backtrace(frames: &mut [Frame]) -> io::Result<(usize, BacktraceContext)> {
+    let mut cx = Context { idx: 0, frames };
+    let result_unwind =
+        unsafe { uw::_Unwind_Backtrace(trace_fn, &mut cx as *mut Context as *mut libc::c_void) };
+    // See libunwind:src/unwind/Backtrace.c for the return values.
+    // No, there is no doc.
+    let res = match result_unwind {
+        // These return codes seem to be benign and need to be ignored for backtraces
+        // to show up properly on all tested platforms.
+        uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => {
+            Ok((cx.idx, BacktraceContext))
+        }
+        _ => Err(io::Error::new(
+            io::ErrorKind::Other,
+            UnwindError(result_unwind),
+        )),
+    };
+    res
+}
+
+extern "C" fn trace_fn(
+    ctx: *mut uw::_Unwind_Context,
+    arg: *mut libc::c_void,
+) -> uw::_Unwind_Reason_Code {
+    let cx = unsafe { &mut *(arg as *mut Context) };
+    if cx.idx >= cx.frames.len() {
+        return uw::_URC_NORMAL_STOP;
+    }
+
+    let mut ip_before_insn = 0;
+    let mut ip = unsafe { uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void };
+    if !ip.is_null() && ip_before_insn == 0 {
+        // this is a non-signaling frame, so `ip` refers to the address
+        // after the calling instruction. account for that.
+        ip = (ip as usize - 1) as *mut _;
+    }
+
+    let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) };
+    cx.frames[cx.idx] = Frame {
+        symbol_addr: symaddr as *mut u8,
+        exact_position: ip as *mut u8,
+        inline_context: 0,
+    };
+    cx.idx += 1;
+
+    uw::_URC_NO_REASON
 }
 
-pub fn resolve_symname<F>(_frame: Frame,
-                          _callback: F,
+// To reduce TCB size in Sgx enclave, we do not want to implement resolve_symname functionality.
+// Rather, we print the offset of the address here, which could be later mapped to correct function.
+pub fn resolve_symname<F>(frame: Frame,
+                          callback: F,
                           _: &BacktraceContext) -> io::Result<()>
     where F: FnOnce(Option<&str>) -> io::Result<()>
 {
-    unsupported()
+    callback(Some(&format!("0x{:x}",
+            (frame.symbol_addr.wrapping_offset_from(image_base() as _)))))
 }
 
 pub fn foreach_symbol_fileline<F>(_: Frame,
@@ -23,5 +92,5 @@ pub fn foreach_symbol_fileline<F>(_: Frame,
                                   _: &BacktraceContext) -> io::Result<bool>
     where F: FnMut(&[u8], u32) -> io::Result<()>
 {
-    unsupported()
+    Ok(false)
 }