Skip to content

Commit 04e51eb

Browse files
committed
Rollup merge of rust-lang#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
2 parents 3b15bd2 + 885cf2a commit 04e51eb

File tree

5 files changed

+168
-30
lines changed

5 files changed

+168
-30
lines changed

src/libpanic_unwind/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ cfg_if! {
6262
if #[cfg(target_os = "emscripten")] {
6363
#[path = "emcc.rs"]
6464
mod imp;
65-
} else if #[cfg(any(target_arch = "wasm32", target_env = "sgx"))] {
65+
} else if #[cfg(target_arch = "wasm32")] {
6666
#[path = "dummy.rs"]
6767
mod imp;
6868
} else if #[cfg(all(target_env = "msvc", target_arch = "aarch64"))] {

src/librustc_target/spec/x86_64_fortanix_unknown_sgx.rs

+27-19
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,29 @@
1010

1111
use std::iter;
1212

13-
use super::{LinkerFlavor, Target, TargetOptions, PanicStrategy};
13+
use super::{LinkerFlavor, PanicStrategy, Target, TargetOptions};
1414

1515
pub fn target() -> Result<Target, String> {
1616
const PRE_LINK_ARGS: &[&str] = &[
1717
"-Wl,--as-needed",
1818
"-Wl,-z,noexecstack",
1919
"-m64",
20-
"-fuse-ld=gold",
21-
"-nostdlib",
22-
"-shared",
23-
"-Wl,-e,sgx_entry",
24-
"-Wl,-Bstatic",
25-
"-Wl,--gc-sections",
26-
"-Wl,-z,text",
27-
"-Wl,-z,norelro",
28-
"-Wl,--rosegment",
29-
"-Wl,--no-undefined",
30-
"-Wl,--error-unresolved-symbols",
31-
"-Wl,--no-undefined-version",
32-
"-Wl,-Bsymbolic",
33-
"-Wl,--export-dynamic",
20+
"-fuse-ld=gold",
21+
"-nostdlib",
22+
"-shared",
23+
"-Wl,-e,sgx_entry",
24+
"-Wl,-Bstatic",
25+
"-Wl,--gc-sections",
26+
"-Wl,-z,text",
27+
"-Wl,-z,norelro",
28+
"-Wl,--rosegment",
29+
"-Wl,--no-undefined",
30+
"-Wl,--error-unresolved-symbols",
31+
"-Wl,--no-undefined-version",
32+
"-Wl,-Bsymbolic",
33+
"-Wl,--export-dynamic",
3434
];
35+
3536
const EXPORT_SYMBOLS: &[&str] = &[
3637
"sgx_entry",
3738
"HEAP_BASE",
@@ -41,19 +42,26 @@ pub fn target() -> Result<Target, String> {
4142
"ENCLAVE_SIZE",
4243
"CFGDATA_BASE",
4344
"DEBUG",
45+
"EH_FRM_HDR_BASE",
46+
"EH_FRM_HDR_SIZE",
47+
"TEXT_BASE",
48+
"TEXT_SIZE",
4449
];
4550
let opts = TargetOptions {
4651
dynamic_linking: false,
4752
executables: true,
4853
linker_is_gnu: true,
4954
max_atomic_width: Some(64),
50-
panic_strategy: PanicStrategy::Abort,
55+
panic_strategy: PanicStrategy::Unwind,
5156
cpu: "x86-64".into(),
5257
features: "+rdrnd,+rdseed".into(),
5358
position_independent_executables: true,
54-
pre_link_args: iter::once(
55-
(LinkerFlavor::Gcc, PRE_LINK_ARGS.iter().cloned().map(String::from).collect())
56-
).collect(),
59+
pre_link_args: iter::once((
60+
LinkerFlavor::Gcc,
61+
PRE_LINK_ARGS.iter().cloned().map(String::from).collect(),
62+
))
63+
.collect(),
64+
post_link_objects: vec!["libunwind.a".into()],
5765
override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(String::from).collect()),
5866
..Default::default()
5967
};

src/libstd/sys/sgx/abi/entry.S

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ IMAGE_BASE:
5656
globvar CFGDATA_BASE 8
5757
/* Non-zero if debugging is enabled, zero otherwise */
5858
globvar DEBUG 1
59+
/* The base address (relative to enclave start) of the enclave text section */
60+
globvar TEXT_BASE 8
61+
/* The size in bytes of enclacve text section */
62+
globvar TEXT_SIZE 8
63+
/* The base address (relative to enclave start) of the enclave EH_FRM_HDR section */
64+
globvar EH_FRM_HDR_BASE 8
65+
/* The size in bytes of enclacve EH_FRM_HDR section */
66+
globvar EH_FRM_HDR_SIZE 8
5967

6068
.Lreentry_panic_msg:
6169
.asciz "Re-entered panicked enclave!"

src/libstd/sys/sgx/rwlock.rs

+131-6
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,33 @@
99
// except according to those terms.
1010

1111
use num::NonZeroUsize;
12+
use slice;
13+
use str;
1214

13-
use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false};
15+
use super::waitqueue::{
16+
try_lock_or_false, NotifiedTcs, SpinMutex, SpinMutexGuard, WaitQueue, WaitVariable,
17+
};
18+
use mem;
1419

1520
pub struct RWLock {
1621
readers: SpinMutex<WaitVariable<Option<NonZeroUsize>>>,
1722
writer: SpinMutex<WaitVariable<bool>>,
1823
}
1924

25+
// Below is to check at compile time, that RWLock has size of 128 bytes.
26+
#[allow(dead_code)]
27+
unsafe fn rw_lock_size_assert(r: RWLock) {
28+
mem::transmute::<RWLock, [u8; 128]>(r);
29+
}
30+
2031
//unsafe impl Send for RWLock {}
2132
//unsafe impl Sync for RWLock {} // FIXME
2233

2334
impl RWLock {
2435
pub const fn new() -> RWLock {
2536
RWLock {
2637
readers: SpinMutex::new(WaitVariable::new(None)),
27-
writer: SpinMutex::new(WaitVariable::new(false))
38+
writer: SpinMutex::new(WaitVariable::new(false)),
2839
}
2940
}
3041

@@ -89,9 +100,11 @@ impl RWLock {
89100
}
90101

91102
#[inline]
92-
pub unsafe fn read_unlock(&self) {
93-
let mut rguard = self.readers.lock();
94-
let wguard = self.writer.lock();
103+
unsafe fn __read_unlock(
104+
&self,
105+
mut rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
106+
wguard: SpinMutexGuard<WaitVariable<bool>>,
107+
) {
95108
*rguard.lock_var_mut() = NonZeroUsize::new(rguard.lock_var().unwrap().get() - 1);
96109
if rguard.lock_var().is_some() {
97110
// There are other active readers
@@ -107,9 +120,18 @@ impl RWLock {
107120
}
108121

109122
#[inline]
110-
pub unsafe fn write_unlock(&self) {
123+
pub unsafe fn read_unlock(&self) {
111124
let rguard = self.readers.lock();
112125
let wguard = self.writer.lock();
126+
self.__read_unlock(rguard, wguard);
127+
}
128+
129+
#[inline]
130+
unsafe fn __write_unlock(
131+
&self,
132+
rguard: SpinMutexGuard<WaitVariable<Option<NonZeroUsize>>>,
133+
wguard: SpinMutexGuard<WaitVariable<bool>>,
134+
) {
113135
if let Err(mut wguard) = WaitQueue::notify_one(wguard) {
114136
// No writers waiting, release the write lock
115137
*wguard.lock_var_mut() = false;
@@ -128,6 +150,109 @@ impl RWLock {
128150
}
129151
}
130152

153+
#[inline]
154+
pub unsafe fn write_unlock(&self) {
155+
let rguard = self.readers.lock();
156+
let wguard = self.writer.lock();
157+
self.__write_unlock(rguard, wguard);
158+
}
159+
160+
#[inline]
161+
unsafe fn unlock(&self) {
162+
let rguard = self.readers.lock();
163+
let wguard = self.writer.lock();
164+
if *wguard.lock_var() == true {
165+
self.__write_unlock(rguard, wguard);
166+
} else {
167+
self.__read_unlock(rguard, wguard);
168+
}
169+
}
170+
131171
#[inline]
132172
pub unsafe fn destroy(&self) {}
133173
}
174+
175+
const EINVAL: i32 = 22;
176+
177+
#[no_mangle]
178+
pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 {
179+
if p.is_null() {
180+
return EINVAL;
181+
}
182+
(*p).read();
183+
return 0;
184+
}
185+
186+
#[no_mangle]
187+
pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 {
188+
if p.is_null() {
189+
return EINVAL;
190+
}
191+
(*p).write();
192+
return 0;
193+
}
194+
#[no_mangle]
195+
pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 {
196+
if p.is_null() {
197+
return EINVAL;
198+
}
199+
(*p).unlock();
200+
return 0;
201+
}
202+
203+
#[no_mangle]
204+
pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) {
205+
if s < 0 {
206+
return;
207+
}
208+
let buf = slice::from_raw_parts(m as *const u8, s as _);
209+
if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) {
210+
eprint!("{}", s);
211+
}
212+
}
213+
214+
#[no_mangle]
215+
pub unsafe extern "C" fn __rust_abort() {
216+
::sys::abort_internal();
217+
}
218+
219+
#[cfg(test)]
220+
mod tests {
221+
222+
use super::*;
223+
use core::array::FixedSizeArray;
224+
use mem::MaybeUninit;
225+
use {mem, ptr};
226+
227+
// The below test verifies that the bytes of initialized RWLock are the ones
228+
// we use in libunwind.
229+
// If they change we need to update src/UnwindRustSgx.h in libunwind.
230+
#[test]
231+
fn test_c_rwlock_initializer() {
232+
const RWLOCK_INIT: &[u8] = &[
233+
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
234+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
235+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
236+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
237+
0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
238+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
239+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
240+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
241+
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
242+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
243+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
244+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
245+
0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
246+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
247+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
248+
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
249+
];
250+
251+
let mut init = MaybeUninit::<RWLock>::zeroed();
252+
init.set(RWLock::new());
253+
assert_eq!(
254+
mem::transmute::<_, [u8; 128]>(init.into_inner()).as_slice(),
255+
RWLOCK_INIT
256+
);
257+
}
258+
}

src/libunwind/lib.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,7 @@ mod macros;
2626
cfg_if! {
2727
if #[cfg(target_env = "msvc")] {
2828
// no extra unwinder support needed
29-
} else if #[cfg(any(
30-
all(target_arch = "wasm32", not(target_os = "emscripten")),
31-
target_env = "sgx"
32-
))] {
29+
} else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] {
3330
// no unwinder on the system!
3431
} else {
3532
extern crate libc;

0 commit comments

Comments
 (0)