about summary refs log tree commit diff
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2018-12-23 00:07:47 +0800
committerkennytm <kennytm@gmail.com>2018-12-23 02:12:07 +0800
commit10d949aa3eab2f4f27ac50c5fbcb95ffbe8b11e5 (patch)
tree57d542dcc1498e8f8fa1d1033f4879d592bf3da9
parentde2ba78dfe6ad7a2502c3708e4719f364e7022ee (diff)
parent885cf2a2afd6da270287cfc3bfa651ac737d0378 (diff)
Rollup merge of #56979 - VardhanThigle:Vardhan/rust-sgx-unwind-support, r=alexcrichton
Adding unwinding support for x86_64_fortanix_unknown_sgx target.

Unwinding support is provided by our port of LLVM's libunwind which is available from https://github.com/fortanix/libunwind/tree/release_50.

libunwind requires support for rwlock and printing to stderr, which is only provided by `std` for this target. This poses two problems: 1) how to expose the `std` functionality to C and 2) dependency inversion.

### Exposing `std`

For exposing the functionality we chose to expose the following symbols:

* __rust_rwlock_rdlock
* __rust_rwlock_wrlock
* __rust_rwlock_unlock
* __rust_print_err
* __rust_abort

Also, the following are needed from `alloc`:

* __rust_alloc
* __rust_dealloc

#### Rust RWLock in C

In `libunwind`, RWLock is initialized as a templated static variable:

```c
pthread_rwlock_t DwarfFDECache<A>::_lock = PTHREAD_RWLOCK_INITIALIZER;
```

I don't know of a good way to use the Rust sys::rwlock::RWLock type and initializer there. We could have a static global variable in Rust, but that doesn't work with the templating. The variable needs to be initialized statically, since this target doesn't support the .init section. Currently, I just used a byte array and standard C array initialization. The mapping between this C type and the Rust type needs to be manually maintained. There is a compile-time check and a unit test to make sure the Rust versions of these C definitions match the actual Rust type. If any reviewer knows of a better solution, please do tell.

### Dependency inversion issue

`std` depends on `panic_unwind` which depends on `libunwind`, and `libunwind` depends on `std`. This is not normally supported by Rust's linking system. Therefore we use raw C exports from `std` *and* `libunwind.a` is linked last in the target `post_link_objects` instead of being built as part of the Rust `libunwind`. Currently, all C exports are defined in `src/libstd/sys/sgx/rwlock.rs` to overcome LTO issues. Only the `__rust_rwlock_*` definitions *need* to live there for privacy reasons. Once again, if any reviewer knows of a better solution, please do tell.

r? @alexcrichton
-rw-r--r--src/libpanic_unwind/lib.rs2
-rw-r--r--src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs46
-rw-r--r--src/libstd/sys/sgx/abi/entry.S8
-rw-r--r--src/libstd/sys/sgx/rwlock.rs137
-rw-r--r--src/libunwind/lib.rs5
5 files changed, 168 insertions, 30 deletions
diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs
index 49f8a429126..cfe671c626b 100644
--- a/src/libpanic_unwind/lib.rs
+++ b/src/libpanic_unwind/lib.rs
@@ -62,7 +62,7 @@ cfg_if! {
     if #[cfg(target_os = "emscripten")] {
         #[path = "emcc.rs"]
         mod imp;
-    } else if #[cfg(any(target_arch = "wasm32", target_env = "sgx"))] {
+    } else if #[cfg(target_arch = "wasm32")] {
         #[path = "dummy.rs"]
         mod imp;
     } else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {
diff --git a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs
index 5b6d8abc5ef..6a6aab2bea3 100644
--- a/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs
+++ b/src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs
@@ -10,28 +10,29 @@
 
 use std::iter;
 
-use super::{LinkerFlavor, Target, TargetOptions, PanicStrategy};
+use super::{LinkerFlavor, PanicStrategy, Target, TargetOptions};
 
 pub fn target() -> Result<Target, String> {
     const PRE_LINK_ARGS: &[&str] = &[
         "-Wl,--as-needed",
         "-Wl,-z,noexecstack",
         "-m64",
-         "-fuse-ld=gold",
-         "-nostdlib",
-         "-shared",
-         "-Wl,-e,sgx_entry",
-         "-Wl,-Bstatic",
-         "-Wl,--gc-sections",
-         "-Wl,-z,text",
-         "-Wl,-z,norelro",
-         "-Wl,--rosegment",
-         "-Wl,--no-undefined",
-         "-Wl,--error-unresolved-symbols",
-         "-Wl,--no-undefined-version",
-         "-Wl,-Bsymbolic",
-         "-Wl,--export-dynamic",
+        "-fuse-ld=gold",
+        "-nostdlib",
+        "-shared",
+        "-Wl,-e,sgx_entry",
+        "-Wl,-Bstatic",
+        "-Wl,--gc-sections",
+        "-Wl,-z,text",
+        "-Wl,-z,norelro",
+        "-Wl,--rosegment",
+        "-Wl,--no-undefined",
+        "-Wl,--error-unresolved-symbols",
+        "-Wl,--no-undefined-version",
+        "-Wl,-Bsymbolic",
+        "-Wl,--export-dynamic",
     ];
+
     const EXPORT_SYMBOLS: &[&str] = &[
         "sgx_entry",
         "HEAP_BASE",
@@ -41,19 +42,26 @@ pub fn target() -> Result<Target, String> {
         "ENCLAVE_SIZE",
         "CFGDATA_BASE",
         "DEBUG",
+        "EH_FRM_HDR_BASE",
+        "EH_FRM_HDR_SIZE",
+        "TEXT_BASE",
+        "TEXT_SIZE",
     ];
     let opts = TargetOptions {
         dynamic_linking: false,
         executables: true,
         linker_is_gnu: true,
         max_atomic_width: Some(64),
-        panic_strategy: PanicStrategy::Abort,
+        panic_strategy: PanicStrategy::Unwind,
         cpu: "x86-64".into(),
         features: "+rdrnd,+rdseed".into(),
         position_independent_executables: true,
-        pre_link_args: iter::once(
-                (LinkerFlavor::Gcc, PRE_LINK_ARGS.iter().cloned().map(String::from).collect())
-        ).collect(),
+        pre_link_args: iter::once((
+            LinkerFlavor::Gcc,
+            PRE_LINK_ARGS.iter().cloned().map(String::from).collect(),
+        ))
+        .collect(),
+        post_link_objects: vec!["libunwind.a".into()],
         override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(String::from).collect()),
         ..Default::default()
     };
diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S
index 4d5cc02e11e..49ede0674ce 100644
--- a/src/libstd/sys/sgx/abi/entry.S
+++ b/src/libstd/sys/sgx/abi/entry.S
@@ -56,6 +56,14 @@ IMAGE_BASE:
     globvar CFGDATA_BASE 8
     /*  Non-zero if debugging is enabled, zero otherwise */
     globvar DEBUG 1
+    /*  The base address (relative to enclave start) of the enclave text section */
+    globvar TEXT_BASE 8
+    /*  The size in bytes of enclacve text section */
+    globvar TEXT_SIZE 8
+    /*  The base address (relative to enclave start) of the enclave EH_FRM_HDR section */
+    globvar EH_FRM_HDR_BASE 8
+    /*  The size in bytes of enclacve EH_FRM_HDR section */
+    globvar EH_FRM_HDR_SIZE 8
 
 .Lreentry_panic_msg:
     .asciz "Re-entered panicked enclave!"
diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs
index a1551dbb53b..d1af98bd4f5 100644
--- a/src/libstd/sys/sgx/rwlock.rs
+++ b/src/libstd/sys/sgx/rwlock.rs
@@ -9,14 +9,25 @@
 // except according to those terms.
 
 use num::NonZeroUsize;
+use slice;
+use str;
 
-use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false};
+use super::waitqueue::{
+    try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
+};
+use mem;
 
 pub struct RWLock {
     readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
     writer: SpinMutex<WaitVariable<bool>>,
 }
 
+// Below is to check at compile time, that RWLock has size of 128 bytes.
+#[allow(dead_code)]
+unsafe fn rw_lock_size_assert(r: RWLock) {
+    mem::transmute::<RWLock, [u8; 128]>(r);
+}
+
 //unsafe impl Send for RWLock {}
 //unsafe impl Sync for RWLock {} // FIXME
 
@@ -24,7 +35,7 @@ impl RWLock {
     pub const fn new() -> RWLock {
         RWLock {
             readers: SpinMutex::new(WaitVariable::new(None)),
-            writer: SpinMutex::new(WaitVariable::new(false))
+            writer: SpinMutex::new(WaitVariable::new(false)),
         }
     }
 
@@ -89,9 +100,11 @@ impl RWLock {
     }
 
     #[inline]
-    pub unsafe fn read_unlock(&self) {
-        let mut rguard = self.readers.lock();
-        let wguard = self.writer.lock();
+    unsafe fn __read_unlock(
+        &self,
+        mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
+        wguard: SpinMutexGuard<WaitVariable<bool>>,
+    ) {
         *rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
         if rguard.lock_var().is_some() {
             // There are other active readers
@@ -107,9 +120,18 @@ impl RWLock {
     }
 
     #[inline]
-    pub unsafe fn write_unlock(&self) {
+    pub unsafe fn read_unlock(&self) {
         let rguard = self.readers.lock();
         let wguard = self.writer.lock();
+        self.__read_unlock(rguard, wguard);
+    }
+
+    #[inline]
+    unsafe fn __write_unlock(
+        &self,
+        rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
+        wguard: SpinMutexGuard<WaitVariable<bool>>,
+    ) {
         if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
             // No writers waiting, release the write lock
             *wguard.lock_var_mut() = false;
@@ -129,5 +151,108 @@ impl RWLock {
     }
 
     #[inline]
+    pub unsafe fn write_unlock(&self) {
+        let rguard = self.readers.lock();
+        let wguard = self.writer.lock();
+        self.__write_unlock(rguard, wguard);
+    }
+
+    #[inline]
+    unsafe fn unlock(&self) {
+        let rguard = self.readers.lock();
+        let wguard = self.writer.lock();
+        if *wguard.lock_var() == true {
+            self.__write_unlock(rguard, wguard);
+        } else {
+            self.__read_unlock(rguard, wguard);
+        }
+    }
+
+    #[inline]
     pub unsafe fn destroy(&self) {}
 }
+
+const EINVAL: i32 = 22;
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
+    if p.is_null() {
+        return EINVAL;
+    }
+    (*p).read();
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
+    if p.is_null() {
+        return EINVAL;
+    }
+    (*p).write();
+    return 0;
+}
+#[no_mangle]
+pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
+    if p.is_null() {
+        return EINVAL;
+    }
+    (*p).unlock();
+    return 0;
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
+    if s < 0 {
+        return;
+    }
+    let buf = slice::from_raw_parts(m as *const u8, s as _);
+    if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) {
+        eprint!("{}", s);
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn __rust_abort() {
+    ::sys::abort_internal();
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+    use core::array::FixedSizeArray;
+    use mem::MaybeUninit;
+    use {mem, ptr};
+
+    // The below test verifies that the bytes of initialized RWLock are the ones
+    // we use in libunwind.
+    // If they change we need to update src/UnwindRustSgx.h in libunwind.
+    #[test]
+    fn test_c_rwlock_initializer() {
+        const RWLOCK_INIT: &[u8] = &[
+            0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+        ];
+
+        let mut init = MaybeUninit::<RWLock>::zeroed();
+        init.set(RWLock::new());
+        assert_eq!(
+            mem::transmute::<_, [u8; 128]>(init.into_inner()).as_slice(),
+            RWLOCK_INIT
+        );
+    }
+}
diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs
index eb53332ab33..954eb9d6d03 100644
--- a/src/libunwind/lib.rs
+++ b/src/libunwind/lib.rs
@@ -26,10 +26,7 @@ mod macros;
 cfg_if! {
     if #[cfg(target_env = "msvc")] {
         // no extra unwinder support needed
-    } else if #[cfg(any(
-        all(target_arch = "wasm32", not(target_os = "emscripten")),
-        target_env = "sgx"
-    ))] {
+    } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] {
         // no unwinder on the system!
     } else {
         extern crate libc;