diff options
| author | kennytm <kennytm@gmail.com> | 2018-12-23 00:07:47 +0800 |
|---|---|---|
| committer | kennytm <kennytm@gmail.com> | 2018-12-23 02:12:07 +0800 |
| commit | 10d949aa3eab2f4f27ac50c5fbcb95ffbe8b11e5 (patch) | |
| tree | 57d542dcc1498e8f8fa1d1033f4879d592bf3da9 | |
| parent | de2ba78dfe6ad7a2502c3708e4719f364e7022ee (diff) | |
| parent | 885cf2a2afd6da270287cfc3bfa651ac737d0378 (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.rs | 2 | ||||
| -rw-r--r-- | src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs | 46 | ||||
| -rw-r--r-- | src/libstd/sys/sgx/abi/entry.S | 8 | ||||
| -rw-r--r-- | src/libstd/sys/sgx/rwlock.rs | 137 | ||||
| -rw-r--r-- | src/libunwind/lib.rs | 5 |
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; |
