diff options
| author | Konrad Borowski <konrad@borowski.pw> | 2018-12-23 16:47:11 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-12-23 16:47:11 +0100 |
| commit | 8ac5380ea0204dbdcbc8108d259928b67d5f8ebb (patch) | |
| tree | 174d912756fc2678af50d46ff457f7504750a975 /src/libstd | |
| parent | b4a306c1e648c84f289c63e984941b7faad10af1 (diff) | |
| parent | ddab10a692aab2e2984b5c826ed9d78a57e94851 (diff) | |
| download | rust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.tar.gz rust-8ac5380ea0204dbdcbc8108d259928b67d5f8ebb.zip | |
Merge branch 'master' into copied
Diffstat (limited to 'src/libstd')
127 files changed, 5926 insertions, 1100 deletions
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index cae2f405318..9cee00b9c76 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -17,13 +17,14 @@ alloc = { path = "../liballoc" } panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } core = { path = "../libcore" } -libc = { path = "../rustc/libc_shim" } -compiler_builtins = { path = "../rustc/compiler_builtins_shim" } +libc = { version = "0.2.44", default-features = false, features = ['rustc-dep-of-std'] } +compiler_builtins = { version = "0.1.1" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } +rustc-demangle = { version = "0.1.10", features = ['rustc-dep-of-std'] } [dev-dependencies] -rand = "0.5" +rand = "0.6.1" [target.x86_64-apple-darwin.dependencies] rustc_asan = { path = "../librustc_asan" } @@ -35,8 +36,11 @@ rustc_lsan = { path = "../librustc_lsan" } rustc_msan = { path = "../librustc_msan" } rustc_tsan = { path = "../librustc_tsan" } -[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -dlmalloc = { path = '../rustc/dlmalloc_shim' } +[target.'cfg(any(all(target_arch = "wasm32", not(target_os = "emscripten")), target_env = "sgx"))'.dependencies] +dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } + +[target.x86_64-fortanix-unknown-sgx.dependencies] +fortanix-sgx-abi = { version = "0.3.1", features = ['rustc-dep-of-std'] } [build-dependencies] cc = "1.0" diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 9d6e8c4cafd..7143de55c88 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -68,7 +68,6 @@ fn main() { println!("cargo:rustc-link-lib=advapi32"); println!("cargo:rustc-link-lib=ws2_32"); println!("cargo:rustc-link-lib=userenv"); - println!("cargo:rustc-link-lib=shell32"); } else if target.contains("fuchsia") { println!("cargo:rustc-link-lib=zircon"); println!("cargo:rustc-link-lib=fdio"); @@ -82,7 +81,12 @@ fn main() { } fn build_libbacktrace(target: &str) -> Result<(), ()> { - let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", "")?; + let native = native_lib_boilerplate( + "../libbacktrace".as_ref(), + "libbacktrace", + "backtrace", + "", + )?; let mut build = cc::Build::new(); build diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 536ce2e16a0..9c994d29202 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,6 +11,7 @@ use self::Entry::*; use self::VacantEntryState::*; +use intrinsics::unlikely; use collections::CollectionAllocErr; use cell::Cell; use borrow::Borrow; @@ -353,7 +354,7 @@ const DISPLACEMENT_THRESHOLD: usize = 128; /// *stat += random_stat_buff(); /// ``` /// -/// The easiest way to use `HashMap` with a custom type as key is to derive [`Eq`] and [`Hash`]. +/// The easiest way to use `HashMap` with a custom key type is to derive [`Eq`] and [`Hash`]. /// We must also derive [`PartialEq`]. /// /// [`Eq`]: ../../std/cmp/trait.Eq.html @@ -1992,6 +1993,9 @@ impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> fn search<F>(self, hash: u64, is_match: F, compare_hashes: bool) -> Option<(&'a K, &'a V)> where F: FnMut(&K) -> bool { + if unsafe { unlikely(self.map.table.size() == 0) } { + return None; + } match search_hashed_nonempty(&self.map.table, SafeHash::new(hash), is_match, @@ -3610,7 +3614,7 @@ mod test_map { for i in 1..1001 { assert!(m.insert(i, i).is_none()); - for j in 1..i + 1 { + for j in 1..=i { let r = m.get(&j); assert_eq!(r, Some(&j)); } @@ -3629,7 +3633,7 @@ mod test_map { for i in 1..1001 { assert!(m.remove(&i).is_some()); - for j in 1..i + 1 { + for j in 1..=i { assert!(!m.contains_key(&j)); } diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 4bb3ce0cf44..d3267e4e801 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -342,7 +342,7 @@ impl<T, S> HashSet<T, S> } /// Visits the values representing the difference, - /// i.e. the values that are in `self` but not in `other`. + /// i.e., the values that are in `self` but not in `other`. /// /// # Examples /// @@ -373,7 +373,7 @@ impl<T, S> HashSet<T, S> } /// Visits the values representing the symmetric difference, - /// i.e. the values that are in `self` or in `other` but not in both. + /// i.e., the values that are in `self` or in `other` but not in both. /// /// # Examples /// @@ -401,7 +401,7 @@ impl<T, S> HashSet<T, S> } /// Visits the values representing the intersection, - /// i.e. the values that are both in `self` and `other`. + /// i.e., the values that are both in `self` and `other`. /// /// # Examples /// @@ -427,7 +427,7 @@ impl<T, S> HashSet<T, S> } /// Visits the values representing the union, - /// i.e. all the values in `self` or `other`, without duplicates. + /// i.e., all the values in `self` or `other`, without duplicates. /// /// # Examples /// @@ -598,7 +598,7 @@ impl<T, S> HashSet<T, S> } /// Returns `true` if the set is a subset of another, - /// i.e. `other` contains at least all the values in `self`. + /// i.e., `other` contains at least all the values in `self`. /// /// # Examples /// @@ -620,7 +620,7 @@ impl<T, S> HashSet<T, S> } /// Returns `true` if the set is a superset of another, - /// i.e. `self` contains at least all the values in `other`. + /// i.e., `self` contains at least all the values in `other`. /// /// # Examples /// diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 479e6dccb90..7195175db28 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -740,6 +740,7 @@ impl<K, V> RawTable<K, V> { } } + #[inline] fn new_internal( capacity: usize, fallibility: Fallibility, @@ -755,12 +756,14 @@ impl<K, V> RawTable<K, V> { /// Tries to create a new raw table from a given capacity. If it cannot allocate, /// it returns with AllocErr. + #[inline] pub fn try_new(capacity: usize) -> Result<RawTable<K, V>, CollectionAllocErr> { Self::new_internal(capacity, Fallible) } /// Creates a new raw table from a given capacity. All buckets are /// initially empty. + #[inline] pub fn new(capacity: usize) -> RawTable<K, V> { match Self::new_internal(capacity, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 9066c0b7694..d14efa2e3c7 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -828,7 +828,7 @@ pub mod consts { /// - s390x /// - sparc64 #[stable(feature = "env", since = "1.0.0")] - pub const ARCH: &'static str = super::arch::ARCH; + pub const ARCH: &str = super::arch::ARCH; /// The family of the operating system. Example value is `unix`. /// @@ -837,7 +837,7 @@ pub mod consts { /// - unix /// - windows #[stable(feature = "env", since = "1.0.0")] - pub const FAMILY: &'static str = os::FAMILY; + pub const FAMILY: &str = os::FAMILY; /// A string describing the specific operating system in use. /// Example value is `linux`. @@ -856,7 +856,7 @@ pub mod consts { /// - android /// - windows #[stable(feature = "env", since = "1.0.0")] - pub const OS: &'static str = os::OS; + pub const OS: &str = os::OS; /// Specifies the filename prefix used for shared libraries on this /// platform. Example value is `lib`. @@ -866,7 +866,7 @@ pub mod consts { /// - lib /// - `""` (an empty string) #[stable(feature = "env", since = "1.0.0")] - pub const DLL_PREFIX: &'static str = os::DLL_PREFIX; + pub const DLL_PREFIX: &str = os::DLL_PREFIX; /// Specifies the filename suffix used for shared libraries on this /// platform. Example value is `.so`. @@ -877,7 +877,7 @@ pub mod consts { /// - .dylib /// - .dll #[stable(feature = "env", since = "1.0.0")] - pub const DLL_SUFFIX: &'static str = os::DLL_SUFFIX; + pub const DLL_SUFFIX: &str = os::DLL_SUFFIX; /// Specifies the file extension used for shared libraries on this /// platform that goes after the dot. Example value is `so`. @@ -888,7 +888,7 @@ pub mod consts { /// - dylib /// - dll #[stable(feature = "env", since = "1.0.0")] - pub const DLL_EXTENSION: &'static str = os::DLL_EXTENSION; + pub const DLL_EXTENSION: &str = os::DLL_EXTENSION; /// Specifies the filename suffix used for executable binaries on this /// platform. Example value is `.exe`. @@ -900,7 +900,7 @@ pub mod consts { /// - .pexe /// - `""` (an empty string) #[stable(feature = "env", since = "1.0.0")] - pub const EXE_SUFFIX: &'static str = os::EXE_SUFFIX; + pub const EXE_SUFFIX: &str = os::EXE_SUFFIX; /// Specifies the file extension, if any, used for executable binaries /// on this platform. Example value is `exe`. @@ -910,72 +910,72 @@ pub mod consts { /// - exe /// - `""` (an empty string) #[stable(feature = "env", since = "1.0.0")] - pub const EXE_EXTENSION: &'static str = os::EXE_EXTENSION; + pub const EXE_EXTENSION: &str = os::EXE_EXTENSION; } #[cfg(target_arch = "x86")] mod arch { - pub const ARCH: &'static str = "x86"; + pub const ARCH: &str = "x86"; } #[cfg(target_arch = "x86_64")] mod arch { - pub const ARCH: &'static str = "x86_64"; + pub const ARCH: &str = "x86_64"; } #[cfg(target_arch = "arm")] mod arch { - pub const ARCH: &'static str = "arm"; + pub const ARCH: &str = "arm"; } #[cfg(target_arch = "aarch64")] mod arch { - pub const ARCH: &'static str = "aarch64"; + pub const ARCH: &str = "aarch64"; } #[cfg(target_arch = "mips")] mod arch { - pub const ARCH: &'static str = "mips"; + pub const ARCH: &str = "mips"; } #[cfg(target_arch = "mips64")] mod arch { - pub const ARCH: &'static str = "mips64"; + pub const ARCH: &str = "mips64"; } #[cfg(target_arch = "powerpc")] mod arch { - pub const ARCH: &'static str = "powerpc"; + pub const ARCH: &str = "powerpc"; } #[cfg(target_arch = "powerpc64")] mod arch { - pub const ARCH: &'static str = "powerpc64"; + pub const ARCH: &str = "powerpc64"; } #[cfg(target_arch = "s390x")] mod arch { - pub const ARCH: &'static str = "s390x"; + pub const ARCH: &str = "s390x"; } #[cfg(target_arch = "sparc64")] mod arch { - pub const ARCH: &'static str = "sparc64"; + pub const ARCH: &str = "sparc64"; } #[cfg(target_arch = "le32")] mod arch { - pub const ARCH: &'static str = "le32"; + pub const ARCH: &str = "le32"; } #[cfg(target_arch = "asmjs")] mod arch { - pub const ARCH: &'static str = "asmjs"; + pub const ARCH: &str = "asmjs"; } #[cfg(target_arch = "wasm32")] mod arch { - pub const ARCH: &'static str = "wasm32"; + pub const ARCH: &str = "wasm32"; } #[cfg(test)] diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 7a5353bb60f..e5c5ab83cbc 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -36,12 +36,12 @@ use str; use string; /// `Error` is a trait representing the basic expectations for error values, -/// i.e. values of type `E` in [`Result<T, E>`]. Errors must describe +/// i.e., values of type `E` in [`Result<T, E>`]. Errors must describe /// themselves through the [`Display`] and [`Debug`] traits, and may provide /// cause chain information: /// /// The [`cause`] method is generally used when errors cross "abstraction -/// boundaries", i.e. when a one module must report an error that is "caused" +/// boundaries", i.e., when a one module must report an error that is "caused" /// by an error from a lower-level module. This setup makes it possible for the /// high-level module to provide its own errors that do not commit to any /// particular implementation, but also reveal some of its implementation for @@ -533,6 +533,7 @@ impl<T: Error> Error for Box<T> { Error::description(&**self) } + #[allow(deprecated)] fn cause(&self) -> Option<&dyn Error> { Error::cause(&**self) } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 7d17aaf2f26..209343444a0 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -253,10 +253,10 @@ impl f32 { unsafe { intrinsics::fmaf32(self, a, b) } } - /// Calculates Euclidean division, the matching method for `mod_euc`. + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that - /// `self = n * rhs + self.mod_euc(rhs)`. + /// `self = n * rhs + self.rem_euclid(rhs)`. /// In other words, the result is `self / rhs` rounded to the integer `n` /// such that `self >= n * rhs`. /// @@ -266,14 +266,14 @@ impl f32 { /// #![feature(euclidean_division)] /// let a: f32 = 7.0; /// let b = 4.0; - /// assert_eq!(a.div_euc(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euc(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euc(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euc(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 /// ``` #[inline] #[unstable(feature = "euclidean_division", issue = "49048")] - pub fn div_euc(self, rhs: f32) -> f32 { + pub fn div_euclid(self, rhs: f32) -> f32 { let q = (self / rhs).trunc(); if self % rhs < 0.0 { return if rhs > 0.0 { q - 1.0 } else { q + 1.0 } @@ -281,7 +281,7 @@ impl f32 { q } - /// Calculates the Euclidean modulo (self mod rhs), which is never negative. + /// Calculates the least nonnegative remainder of `self (mod rhs)`. /// /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in /// most cases. However, due to a floating point round-off error it can @@ -289,7 +289,7 @@ impl f32 { /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. /// This result is not an element of the function's codomain, but it is the /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euc(rhs) * rhs + self.mod_euc(rhs)` + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` /// approximatively. /// /// # Examples @@ -298,16 +298,16 @@ impl f32 { /// #![feature(euclidean_division)] /// let a: f32 = 7.0; /// let b = 4.0; - /// assert_eq!(a.mod_euc(b), 3.0); - /// assert_eq!((-a).mod_euc(b), 1.0); - /// assert_eq!(a.mod_euc(-b), 3.0); - /// assert_eq!((-a).mod_euc(-b), 1.0); + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); /// // limitation due to round-off error - /// assert!((-std::f32::EPSILON).mod_euc(3.0) != 0.0); + /// assert!((-std::f32::EPSILON).rem_euclid(3.0) != 0.0); /// ``` #[inline] #[unstable(feature = "euclidean_division", issue = "49048")] - pub fn mod_euc(self, rhs: f32) -> f32 { + pub fn rem_euclid(self, rhs: f32) -> f32 { let r = self % rhs; if r < 0.0 { r + rhs.abs() @@ -550,7 +550,8 @@ impl f32 { #[inline] #[rustc_deprecated(since = "1.10.0", reason = "you probably meant `(self - other).abs()`: \ - this operation is `(self - other).max(0.0)` (also \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ known as `fdimf` in C). If you truly need the positive \ difference, consider using that expression or the C function \ `fdimf`, depending on how you wish to handle NaN (please consider \ diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index ecaaf8323ab..b73a67ed9d8 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -229,10 +229,10 @@ impl f64 { unsafe { intrinsics::fmaf64(self, a, b) } } - /// Calculates Euclidean division, the matching method for `mod_euc`. + /// Calculates Euclidean division, the matching method for `rem_euclid`. /// /// This computes the integer `n` such that - /// `self = n * rhs + self.mod_euc(rhs)`. + /// `self = n * rhs + self.rem_euclid(rhs)`. /// In other words, the result is `self / rhs` rounded to the integer `n` /// such that `self >= n * rhs`. /// @@ -242,14 +242,14 @@ impl f64 { /// #![feature(euclidean_division)] /// let a: f64 = 7.0; /// let b = 4.0; - /// assert_eq!(a.div_euc(b), 1.0); // 7.0 > 4.0 * 1.0 - /// assert_eq!((-a).div_euc(b), -2.0); // -7.0 >= 4.0 * -2.0 - /// assert_eq!(a.div_euc(-b), -1.0); // 7.0 >= -4.0 * -1.0 - /// assert_eq!((-a).div_euc(-b), 2.0); // -7.0 >= -4.0 * 2.0 + /// assert_eq!(a.div_euclid(b), 1.0); // 7.0 > 4.0 * 1.0 + /// assert_eq!((-a).div_euclid(b), -2.0); // -7.0 >= 4.0 * -2.0 + /// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0 + /// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0 /// ``` #[inline] #[unstable(feature = "euclidean_division", issue = "49048")] - pub fn div_euc(self, rhs: f64) -> f64 { + pub fn div_euclid(self, rhs: f64) -> f64 { let q = (self / rhs).trunc(); if self % rhs < 0.0 { return if rhs > 0.0 { q - 1.0 } else { q + 1.0 } @@ -257,7 +257,7 @@ impl f64 { q } - /// Calculates the Euclidean modulo (self mod rhs), which is never negative. + /// Calculates the least nonnegative remainder of `self (mod rhs)`. /// /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in /// most cases. However, due to a floating point round-off error it can @@ -265,7 +265,7 @@ impl f64 { /// `self` is much smaller than `rhs.abs()` in magnitude and `self < 0.0`. /// This result is not an element of the function's codomain, but it is the /// closest floating point number in the real numbers and thus fulfills the - /// property `self == self.div_euc(rhs) * rhs + self.mod_euc(rhs)` + /// property `self == self.div_euclid(rhs) * rhs + self.rem_euclid(rhs)` /// approximatively. /// /// # Examples @@ -274,16 +274,16 @@ impl f64 { /// #![feature(euclidean_division)] /// let a: f64 = 7.0; /// let b = 4.0; - /// assert_eq!(a.mod_euc(b), 3.0); - /// assert_eq!((-a).mod_euc(b), 1.0); - /// assert_eq!(a.mod_euc(-b), 3.0); - /// assert_eq!((-a).mod_euc(-b), 1.0); + /// assert_eq!(a.rem_euclid(b), 3.0); + /// assert_eq!((-a).rem_euclid(b), 1.0); + /// assert_eq!(a.rem_euclid(-b), 3.0); + /// assert_eq!((-a).rem_euclid(-b), 1.0); /// // limitation due to round-off error - /// assert!((-std::f64::EPSILON).mod_euc(3.0) != 0.0); + /// assert!((-std::f64::EPSILON).rem_euclid(3.0) != 0.0); /// ``` #[inline] #[unstable(feature = "euclidean_division", issue = "49048")] - pub fn mod_euc(self, rhs: f64) -> f64 { + pub fn rem_euclid(self, rhs: f64) -> f64 { let r = self % rhs; if r < 0.0 { r + rhs.abs() @@ -491,7 +491,8 @@ impl f64 { #[inline] #[rustc_deprecated(since = "1.10.0", reason = "you probably meant `(self - other).abs()`: \ - this operation is `(self - other).max(0.0)` (also \ + this operation is `(self - other).max(0.0)` \ + except that `abs_sub` also propagates NaNs (also \ known as `fdim` in C). If you truly need the positive \ difference, consider using that expression or the C function \ `fdim`, depending on how you wish to handle NaN (please consider \ @@ -888,7 +889,7 @@ impl f64 { } // Solaris/Illumos requires a wrapper around log, log2, and log10 functions - // because of their non-standard behavior (e.g. log(-n) returns -Inf instead + // because of their non-standard behavior (e.g., log(-n) returns -Inf instead // of expected NaN). fn log_wrapper<F: Fn(f64) -> f64>(self, log_fn: F) -> f64 { if !cfg!(target_os = "solaris") { diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 66718b95408..768998b235e 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -372,7 +372,7 @@ impl CString { /// # Safety /// /// This should only ever be called with a pointer that was earlier - /// obtained by calling [`into_raw`] on a `CString`. Other usage (e.g. trying to take + /// obtained by calling [`into_raw`] on a `CString`. Other usage (e.g., trying to take /// ownership of a string that was allocated by foreign code) is likely to lead /// to undefined behavior or allocator corruption. /// @@ -671,7 +671,7 @@ impl fmt::Debug for CStr { #[stable(feature = "cstr_default", since = "1.10.0")] impl<'a> Default for &'a CStr { fn default() -> &'a CStr { - const SLICE: &'static [c_char] = &[0]; + const SLICE: &[c_char] = &[0]; unsafe { CStr::from_ptr(SLICE.as_ptr()) } } } @@ -1167,8 +1167,8 @@ impl CStr { /// ``` #[stable(feature = "cstr_to_str", since = "1.4.0")] pub fn to_str(&self) -> Result<&str, str::Utf8Error> { - // NB: When CStr is changed to perform the length check in .to_bytes() - // instead of in from_ptr(), it may be worth considering if this should + // N.B., when `CStr` is changed to perform the length check in `.to_bytes()` + // instead of in `from_ptr()`, it may be worth considering if this should // be rewritten to do the UTF-8 check inline with the length calculation // instead of doing it afterwards. str::from_utf8(self.to_bytes()) @@ -1475,7 +1475,7 @@ mod tests { #[test] fn cstr_const_constructor() { - const CSTR: &'static CStr = unsafe { + const CSTR: &CStr = unsafe { CStr::from_bytes_with_nul_unchecked(b"Hello, world!\0") }; diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index bd5fc3fa24a..7e155396b8d 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -21,7 +21,7 @@ //! Rust represents owned strings with the [`String`] type, and //! borrowed slices of strings with the [`str`] primitive. Both are //! always in UTF-8 encoding, and may contain nul bytes in the middle, -//! i.e. if you look at the bytes that make up the string, there may +//! i.e., if you look at the bytes that make up the string, there may //! be a `\0` among them. Both `String` and `str` store their length //! explicitly; there are no nul terminators at the end of strings //! like in C. @@ -44,7 +44,7 @@ //! code point]'. //! //! * **Nul terminators and implicit string lengths** - Often, C -//! strings are nul-terminated, i.e. they have a `\0` character at the +//! strings are nul-terminated, i.e., they have a `\0` character at the //! end. The length of a string buffer is not stored, but has to be //! calculated; to compute the length of a string, C code must //! manually call a function like `strlen()` for `char`-based strings, @@ -72,32 +72,32 @@ //! //! * **From Rust to C:** [`CString`] represents an owned, C-friendly //! string: it is nul-terminated, and has no internal nul characters. -//! Rust code can create a `CString` out of a normal string (provided +//! Rust code can create a [`CString`] out of a normal string (provided //! that the string doesn't have nul characters in the middle), and -//! then use a variety of methods to obtain a raw `*mut u8` that can +//! then use a variety of methods to obtain a raw `*mut `[`u8`] that can //! then be passed as an argument to functions which use the C //! conventions for strings. //! //! * **From C to Rust:** [`CStr`] represents a borrowed C string; it -//! is what you would use to wrap a raw `*const u8` that you got from -//! a C function. A `CStr` is guaranteed to be a nul-terminated array -//! of bytes. Once you have a `CStr`, you can convert it to a Rust -//! `&str` if it's valid UTF-8, or lossily convert it by adding +//! is what you would use to wrap a raw `*const `[`u8`] that you got from +//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array +//! of bytes. Once you have a [`CStr`], you can convert it to a Rust +//! [`&str`][`str`] if it's valid UTF-8, or lossily convert it by adding //! replacement characters. //! //! [`OsString`] and [`OsStr`] are useful when you need to transfer //! strings to and from the operating system itself, or when capturing -//! the output of external commands. Conversions between `OsString`, -//! `OsStr` and Rust strings work similarly to those for [`CString`] +//! the output of external commands. Conversions between [`OsString`], +//! [`OsStr`] and Rust strings work similarly to those for [`CString`] //! and [`CStr`]. //! //! * [`OsString`] represents an owned string in whatever //! representation the operating system prefers. In the Rust standard //! library, various APIs that transfer strings to/from the operating -//! system use `OsString` instead of plain strings. For example, +//! system use [`OsString`] instead of plain strings. For example, //! [`env::var_os()`] is used to query environment variables; it -//! returns an `Option<OsString>`. If the environment variable exists -//! you will get a `Some(os_string)`, which you can *then* try to +//! returns an [`Option`]`<`[`OsString`]`>`. If the environment variable +//! exists you will get a [`Some`]`(os_string)`, which you can *then* try to //! convert to a Rust string. This yields a [`Result<>`], so that //! your code can detect errors in case the environment variable did //! not in fact contain valid Unicode data. @@ -105,7 +105,7 @@ //! * [`OsStr`] represents a borrowed reference to a string in a //! format that can be passed to the operating system. It can be //! converted into an UTF-8 Rust string slice in a similar way to -//! `OsString`. +//! [`OsString`]. //! //! # Conversions //! @@ -131,7 +131,7 @@ //! Additionally, on Windows [`OsString`] implements the //! `std::os::windows:ffi::`[`OsStringExt`][windows.OsStringExt] //! trait, which provides a [`from_wide`] method. The result of this -//! method is an `OsString` which can be round-tripped to a Windows +//! method is an [`OsString`] which can be round-tripped to a Windows //! string losslessly. //! //! [`String`]: ../string/struct.String.html @@ -160,6 +160,8 @@ //! [`collect`]: ../iter/trait.Iterator.html#method.collect //! [windows.OsStringExt]: ../os/windows/ffi/trait.OsStringExt.html //! [`from_wide`]: ../os/windows/ffi/trait.OsStringExt.html#tymethod.from_wide +//! [`Option`]: ../option/enum.Option.html +//! [`Some`]: ../option/enum.Option.html#variant.Some #![stable(feature = "rust1", since = "1.0.0")] @@ -174,7 +176,6 @@ pub use self::os_str::{OsString, OsStr}; #[stable(feature = "raw_os", since = "1.1.0")] pub use core::ffi::c_void; -#[cfg(not(stage0))] #[unstable(feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ all supported platforms", diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 9c40a31986c..766142fb57f 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -36,7 +36,7 @@ use sys_common::{AsInner, IntoInner, FromInner}; /// and platform-native string values, and in particular allowing a Rust string /// to be converted into an "OS" string with no cost if possible. A consequence /// of this is that `OsString` instances are *not* `NUL` terminated; in order -/// to pass to e.g. Unix system call, you should create a [`CStr`]. +/// to pass to e.g., Unix system call, you should create a [`CStr`]. /// /// `OsString` is to [`&OsStr`] as [`String`] is to [`&str`]: the former /// in each pair are owned strings; the latter are borrowed @@ -536,17 +536,42 @@ impl OsStr { /// /// # Examples /// - /// Calling `to_string_lossy` on an `OsStr` with valid unicode: + /// Calling `to_string_lossy` on an `OsStr` with invalid unicode: /// /// ``` - /// use std::ffi::OsStr; - /// - /// let os_str = OsStr::new("foo"); - /// assert_eq!(os_str.to_string_lossy(), "foo"); + /// // Note, due to differences in how Unix and Windows represent strings, + /// // we are forced to complicate this example, setting up example `OsStr`s + /// // with different source data and via different platform extensions. + /// // Understand that in reality you could end up with such example invalid + /// // sequences simply through collecting user command line arguments, for + /// // example. + /// + /// #[cfg(any(unix, target_os = "redox"))] { + /// use std::ffi::OsStr; + /// use std::os::unix::ffi::OsStrExt; + /// + /// // Here, the values 0x66 and 0x6f correspond to 'f' and 'o' + /// // respectively. The value 0x80 is a lone continuation byte, invalid + /// // in a UTF-8 sequence. + /// let source = [0x66, 0x6f, 0x80, 0x6f]; + /// let os_str = OsStr::from_bytes(&source[..]); + /// + /// assert_eq!(os_str.to_string_lossy(), "fo�o"); + /// } + /// #[cfg(windows)] { + /// use std::ffi::OsString; + /// use std::os::windows::prelude::*; + /// + /// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' + /// // respectively. The value 0xD800 is a lone surrogate half, invalid + /// // in a UTF-16 sequence. + /// let source = [0x0066, 0x006f, 0xD800, 0x006f]; + /// let os_string = OsString::from_wide(&source[..]); + /// let os_str = os_string.as_os_str(); + /// + /// assert_eq!(os_str.to_string_lossy(), "fo�o"); + /// } /// ``` - /// - /// Had `os_str` contained invalid unicode, the `to_string_lossy` call might - /// have returned `"fo�"`. #[stable(feature = "rust1", since = "1.0.0")] pub fn to_string_lossy(&self) -> Cow<str> { self.inner.to_string_lossy() diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 7d054a347f4..35ae4939249 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -195,9 +195,10 @@ pub struct OpenOptions(fs_imp::OpenOptions); /// This module only currently provides one bit of information, [`readonly`], /// which is exposed on all currently supported platforms. Unix-specific /// functionality, such as mode bits, is available through the -/// `os::unix::PermissionsExt` trait. +/// [`PermissionsExt`] trait. /// /// [`readonly`]: struct.Permissions.html#method.readonly +/// [`PermissionsExt`]: ../os/unix/fs/trait.PermissionsExt.html #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Permissions(fs_imp::FilePermissions); @@ -1406,7 +1407,7 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// Removes a file from the filesystem. /// /// Note that there is no -/// guarantee that the file is immediately deleted (e.g. depending on +/// guarantee that the file is immediately deleted (e.g., depending on /// platform, other open file descriptors may prevent immediate removal). /// /// # Platform-specific behavior @@ -1728,7 +1729,7 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { /// limited to just these cases: /// /// * `path` does not exist. -/// * A component in path is not a directory. +/// * A non-final component in path is not a directory. /// /// # Examples /// @@ -2089,7 +2090,7 @@ mod tests { use fs::{self, File, OpenOptions}; use io::{ErrorKind, SeekFrom}; use path::Path; - use rand::{StdRng, FromEntropy, RngCore}; + use rand::{rngs::StdRng, FromEntropy, RngCore}; use str; use sys_common::io::test::{TempDir, tmpdir}; use thread; diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 476ee3f71ca..7aaf89cd0ff 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -294,7 +294,7 @@ impl<R: Seek> Seek for BufReader<R> { /// `.into_inner()` immediately after a seek yields the underlying reader /// at the same position. /// - /// To seek without discarding the internal buffer, use [`Seek::seek_relative`]. + /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. /// /// See [`std::io::Seek`] for more details. /// @@ -303,6 +303,9 @@ impl<R: Seek> Seek for BufReader<R> { /// seeks will be performed instead of one. If the second seek returns /// `Err`, the underlying reader will be left at the same position it would /// have if you called `seek` with `SeekFrom::Current(0)`. + /// + /// [`BufReader::seek_relative`]: struct.BufReader.html#method.seek_relative + /// [`std::io::Seek`]: trait.Seek.html fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { let result: u64; if let SeekFrom::Current(n) = pos { @@ -918,7 +921,7 @@ impl<W: Write> Write for LineWriter<W> { // some data then we *must* report that we wrote that data, so future // errors are ignored. We set our internal `need_flush` flag, though, in // case flushing fails and we need to try it first next time. - let n = self.inner.write(&buf[..i + 1])?; + let n = self.inner.write(&buf[..=i])?; self.need_flush = true; if self.flush().is_err() || n != i + 1 { return Ok(n) diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index 14f20151dca..f7a90333ef2 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -90,7 +90,7 @@ pub struct Cursor<T> { impl<T> Cursor<T> { /// Creates a new cursor wrapping the provided underlying in-memory buffer. /// - /// Cursor initial position is `0` even if underlying buffer (e.g. `Vec`) + /// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`) /// is not empty. So writing to cursor starts with overwriting `Vec` /// content, not with appending to it. /// diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 386de080b85..324852355b0 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -184,7 +184,7 @@ pub enum ErrorKind { } impl ErrorKind { - fn as_str(&self) -> &'static str { + pub(crate) fn as_str(&self) -> &'static str { match *self { ErrorKind::NotFound => "entity not found", ErrorKind::PermissionDenied => "permission denied", @@ -225,6 +225,9 @@ impl From<ErrorKind> for Error { /// let error = Error::from(not_found); /// assert_eq!("entity not found", format!("{}", error)); /// ``` + /// + /// [`ErrorKind`]: ../../std/io/enum.ErrorKind.html + /// [`Error`]: ../../std/io/struct.Error.html #[inline] fn from(kind: ErrorKind) -> Error { Error { @@ -555,6 +558,7 @@ impl error::Error for Error { } } + #[allow(deprecated)] fn cause(&self) -> Option<&dyn error::Error> { match self.repr { Repr::Os(..) => None, diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 076524e624a..5137a9432ae 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -271,6 +271,7 @@ use cmp; use fmt; +use slice; use str; use memchr; use ptr; @@ -1261,7 +1262,7 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) }; match memchr::memchr(delim, available) { Some(i) => { - buf.extend_from_slice(&available[..i + 1]); + buf.extend_from_slice(&available[..=i]); (true, i + 1) } None => { @@ -1936,18 +1937,6 @@ impl<T: BufRead> BufRead for Take<T> { } } -fn read_one_byte(reader: &mut dyn Read) -> Option<Result<u8>> { - let mut buf = [0]; - loop { - return match reader.read(&mut buf) { - Ok(0) => None, - Ok(..) => Some(Ok(buf[0])), - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => Some(Err(e)), - }; - } -} - /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes`] on a reader. @@ -1965,7 +1954,15 @@ impl<R: Read> Iterator for Bytes<R> { type Item = Result<u8>; fn next(&mut self) -> Option<Result<u8>> { - read_one_byte(&mut self.inner) + let mut byte = 0; + loop { + return match self.inner.read(slice::from_mut(&mut byte)) { + Ok(0) => None, + Ok(..) => Some(Ok(byte)), + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => Some(Err(e)), + }; + } } } diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index a413432cdaa..8c03f355848 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -131,7 +131,7 @@ fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { /// /// Each handle is a shared reference to a global buffer of input data to this /// process. A handle can be `lock`'d to gain full access to [`BufRead`] methods -/// (e.g. `.lines()`). Reads to this handle are otherwise locked with respect +/// (e.g., `.lines()`). Reads to this handle are otherwise locked with respect /// to other reads. /// /// This handle implements the `Read` trait, but beware that concurrent reads @@ -269,7 +269,7 @@ impl Stdin { /// /// You can run the example one of two ways: /// - /// - Pipe some text to it, e.g. `printf foo | path/to/executable` + /// - Pipe some text to it, e.g., `printf foo | path/to/executable` /// - Give it text interactively by running the executable directly, /// in which case it will wait for the Enter key to be pressed before /// continuing diff --git a/src/libstd/keyword_docs.rs b/src/libstd/keyword_docs.rs index 13cf3133dcd..12b60313725 100644 --- a/src/libstd/keyword_docs.rs +++ b/src/libstd/keyword_docs.rs @@ -65,7 +65,7 @@ mod as_keyword { } /// look like this: /// /// ```rust -/// const WORDS: &'static str = "hello rust!"; +/// const WORDS: &str = "hello rust!"; /// ``` /// /// Thanks to static lifetime elision, you usually don't have to explicitly use 'static: @@ -188,7 +188,7 @@ mod enum_keyword { } /// For external connections in Rust code. /// /// The `extern` keyword is used in two places in Rust. One is in conjunction with the [`crate`] -/// keyword to make your Rust code aware of other Rust crates in your project, i.e. `extern crate +/// keyword to make your Rust code aware of other Rust crates in your project, i.e., `extern crate /// lazy_static;`. The other use is in foreign function interfaces (FFI). /// /// `extern` is used in two different contexts within FFI. The first is in the form of external diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 78109a1a690..0d3de34fe64 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -222,6 +222,7 @@ #![no_std] #![deny(missing_docs)] +#![deny(intra_doc_link_resolution_failure)] #![deny(missing_debug_implementations)] // Tell the compiler to link to either panic_abort or panic_unwind @@ -250,6 +251,7 @@ #![feature(cfg_target_vendor)] #![feature(char_error_internals)] #![feature(compiler_builtins_lib)] +#![feature(concat_idents)] #![feature(const_int_ops)] #![feature(const_ip)] #![feature(const_raw_ptr_deref)] @@ -271,6 +273,7 @@ #![feature(libc)] #![feature(link_args)] #![feature(linkage)] +#![cfg_attr(not(stage0), feature(min_const_unsafe_fn))] #![feature(needs_panic_runtime)] #![feature(never_type)] #![feature(nll)] @@ -287,7 +290,7 @@ #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(std_internals)] -#![cfg_attr(not(stage0), feature(stdsimd))] +#![feature(stdsimd)] #![feature(shrink_to)] #![feature(slice_concat_ext)] #![feature(slice_internals)] @@ -312,13 +315,11 @@ #![feature(non_exhaustive)] #![feature(alloc_layout_extra)] #![feature(maybe_uninit)] +#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains, slice_index_methods, + decl_macro, coerce_unsized))] #![default_lib_allocator] -#[cfg(stage0)] -#[global_allocator] -static ALLOC: alloc::System = alloc::System; - // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. #[prelude_import] @@ -340,6 +341,7 @@ pub use core::{unreachable, unimplemented, write, writeln, try}; extern crate alloc as alloc_crate; #[doc(masked)] extern crate libc; +extern crate rustc_demangle; // We always need an unwinder currently for backtraces #[doc(masked)] @@ -354,6 +356,12 @@ extern crate unwind; // testing gives test-std access to real-std lang items and globals. See #2912 #[cfg(test)] extern crate std as realstd; +#[cfg(target_env = "sgx")] +#[macro_use] +#[allow(unused_imports)] // FIXME: without `#[macro_use]`, get error: “cannot + // determine resolution for the macro `usercalls_asm`” +extern crate fortanix_sgx_abi; + // The standard macros that are not built-in to the compiler. #[macro_use] mod macros; @@ -506,18 +514,17 @@ pub mod rt; #[path = "../stdsimd/stdsimd/mod.rs"] #[allow(missing_debug_implementations, missing_docs, dead_code)] #[unstable(feature = "stdsimd", issue = "48556")] -#[cfg(all(not(stage0), not(test)))] +#[cfg(not(test))] mod stdsimd; // A "fake" module needed by the `stdsimd` module to compile, not actually // exported though. -#[cfg(not(stage0))] mod coresimd { pub use core::arch; } #[stable(feature = "simd_arch", since = "1.27.0")] -#[cfg(all(not(stage0), not(test)))] +#[cfg(not(test))] pub use stdsimd::arch; // Include a number of private modules that exist solely to provide diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index ff35325ab4f..21def5d93b7 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -16,10 +16,11 @@ use net::{ntoh, hton, IpAddr, Ipv4Addr, Ipv6Addr}; use option; use sys::net::netc as c; use sys_common::{FromInner, AsInner, IntoInner}; -use sys_common::net::lookup_host; +use sys_common::net::LookupHost; use vec; use iter; use slice; +use convert::TryInto; /// An internet socket address, either IPv4 or IPv6. /// @@ -702,7 +703,7 @@ impl hash::Hash for SocketAddrV6 { /// the other: for simple uses a string like `"localhost:12345"` is much nicer /// than manual construction of the corresponding [`SocketAddr`], but sometimes /// [`SocketAddr`] value is *the* main source of the address, and converting it to -/// some other type (e.g. a string) just for it to be converted back to +/// some other type (e.g., a string) just for it to be converted back to /// [`SocketAddr`] in constructor methods is pointless. /// /// Addresses returned by the operating system that are not IP addresses are @@ -863,9 +864,9 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { } } -fn resolve_socket_addr(s: &str, p: u16) -> io::Result<vec::IntoIter<SocketAddr>> { - let ips = lookup_host(s)?; - let v: Vec<_> = ips.map(|mut a| { a.set_port(p); a }).collect(); +fn resolve_socket_addr(lh: LookupHost) -> io::Result<vec::IntoIter<SocketAddr>> { + let p = lh.port(); + let v: Vec<_> = lh.map(|mut a| { a.set_port(p); a }).collect(); Ok(v.into_iter()) } @@ -885,7 +886,7 @@ impl<'a> ToSocketAddrs for (&'a str, u16) { return Ok(vec![SocketAddr::V6(addr)].into_iter()) } - resolve_socket_addr(host, port) + resolve_socket_addr((host, port).try_into()?) } } @@ -899,22 +900,7 @@ impl ToSocketAddrs for str { return Ok(vec![addr].into_iter()); } - macro_rules! try_opt { - ($e:expr, $msg:expr) => ( - match $e { - Some(r) => r, - None => return Err(io::Error::new(io::ErrorKind::InvalidInput, - $msg)), - } - ) - } - - // split the string by ':' and convert the second part to u16 - let mut parts_iter = self.rsplitn(2, ':'); - let port_str = try_opt!(parts_iter.next(), "invalid socket address"); - let host = try_opt!(parts_iter.next(), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - resolve_socket_addr(host, port) + resolve_socket_addr(self.try_into()?) } } diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index be4bcee8a68..ff579a5feb1 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -112,11 +112,15 @@ fn hton<I: NetInt>(i: I) -> I { i.to_be() } fn ntoh<I: NetInt>(i: I) -> I { I::from_be(i) } fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T> - where F: FnMut(&SocketAddr) -> io::Result<T> + where F: FnMut(io::Result<&SocketAddr>) -> io::Result<T> { + let addrs = match addr.to_socket_addrs() { + Ok(addrs) => addrs, + Err(e) => return f(Err(e)) + }; let mut last_err = None; - for addr in addr.to_socket_addrs()? { - match f(&addr) { + for addr in addrs { + match f(Ok(&addr)) { Ok(l) => return Ok(l), Err(e) => last_err = Some(e), } diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index be797803233..347e795c4f7 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -530,7 +530,7 @@ impl TcpStream { /// Moves this TCP stream into or out of nonblocking mode. /// /// This will result in `read`, `write`, `recv` and `send` operations - /// becoming nonblocking, i.e. immediately returning from their calls. + /// becoming nonblocking, i.e., immediately returning from their calls. /// If the IO operation is successful, `Ok` is returned and no further /// action is required. If the IO operation could not be completed and needs /// to be retried, an error with kind [`io::ErrorKind::WouldBlock`] is @@ -729,6 +729,9 @@ impl TcpListener { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + // On WASM, `TcpStream` is uninhabited (as it's unsupported) and so + // the `a` variable here is technically unused. + #[cfg_attr(target_arch = "wasm32", allow(unused_variables))] self.0.accept().map(|(a, b)| (TcpStream(a), b)) } @@ -840,7 +843,7 @@ impl TcpListener { /// Moves this TCP stream into or out of nonblocking mode. /// /// This will result in the `accept` operation becoming nonblocking, - /// i.e. immediately returning from their calls. If the IO operation is + /// i.e., immediately returning from their calls. If the IO operation is /// successful, `Ok` is returned and no further action is required. If the /// IO operation could not be completed and needs to be retried, an error /// with kind [`io::ErrorKind::WouldBlock`] is returned. diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index fc68abae05a..6c0c636d30c 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -752,7 +752,7 @@ impl UdpSocket { /// Moves this UDP socket into or out of nonblocking mode. /// /// This will result in `recv`, `recv_from`, `send`, and `send_to` - /// operations becoming nonblocking, i.e. immediately returning from their + /// operations becoming nonblocking, i.e., immediately returning from their /// calls. If the IO operation is successful, `Ok` is returned and no /// further action is required. If the IO operation could not be completed /// and needs to be retried, an error with kind diff --git a/src/libstd/os/raw/mod.rs b/src/libstd/os/raw/mod.rs index 95faf3a5dd6..05f30f2881a 100644 --- a/src/libstd/os/raw/mod.rs +++ b/src/libstd/os/raw/mod.rs @@ -27,6 +27,10 @@ all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), + all(target_os = "freebsd", any(target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64")), all(target_os = "netbsd", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc")), @@ -42,6 +46,10 @@ all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), + all(target_os = "freebsd", any(target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64")), all(target_os = "netbsd", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc")), diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 4bc18a57e92..3eacc7afc41 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -80,7 +80,7 @@ pub use core::panic::{PanicInfo, Location}; /// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow /// witnessing a broken invariant through the use of `catch_unwind` (catching a /// panic). This trait is an auto trait, so it is automatically implemented for -/// many types, and it is also structurally composed (e.g. a struct is unwind +/// many types, and it is also structurally composed (e.g., a struct is unwind /// safe if all of its components are unwind safe). /// /// Note, however, that this is not an unsafe trait, so there is not a succinct @@ -264,7 +264,7 @@ impl RefUnwindSafe for atomic::AtomicI32 {} #[cfg(target_has_atomic = "64")] #[unstable(feature = "integer_atomics", issue = "32976")] impl RefUnwindSafe for atomic::AtomicI64 {} -#[cfg(all(not(stage0), target_has_atomic = "128"))] +#[cfg(target_has_atomic = "128")] #[unstable(feature = "integer_atomics", issue = "32976")] impl RefUnwindSafe for atomic::AtomicI128 {} @@ -283,7 +283,7 @@ impl RefUnwindSafe for atomic::AtomicU32 {} #[cfg(target_has_atomic = "64")] #[unstable(feature = "integer_atomics", issue = "32976")] impl RefUnwindSafe for atomic::AtomicU64 {} -#[cfg(all(not(stage0), target_has_atomic = "128"))] +#[cfg(target_has_atomic = "128")] #[unstable(feature = "integer_atomics", issue = "32976")] impl RefUnwindSafe for atomic::AtomicU128 {} diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 4930d356608..b6180fbeb50 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -29,7 +29,7 @@ use intrinsics; use mem; use ptr; use raw; -use sys::stdio::{Stderr, stderr_prints_nothing}; +use sys::stdio::panic_output; use sys_common::rwlock::RWLock; use sys_common::thread_info; use sys_common::util; @@ -193,7 +193,6 @@ fn default_hook(info: &PanicInfo) { None => "Box<Any>", } }; - let mut err = Stderr::new().ok(); let thread = thread_info::current_thread(); let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>"); @@ -210,22 +209,20 @@ fn default_hook(info: &PanicInfo) { if let Some(format) = log_backtrace { let _ = backtrace::print(err, format); } else if FIRST_PANIC.compare_and_swap(true, false, Ordering::SeqCst) { - let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` for a backtrace."); + let _ = writeln!(err, "note: Run with `RUST_BACKTRACE=1` \ + environment variable to display a backtrace."); } } }; - let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); - match (prev, err.as_mut()) { - (Some(mut stderr), _) => { - write(&mut *stderr); - let mut s = Some(stderr); - LOCAL_STDERR.with(|slot| { - *slot.borrow_mut() = s.take(); - }); - } - (None, Some(ref mut err)) => { write(err) } - _ => {} + if let Some(mut local) = LOCAL_STDERR.with(|s| s.borrow_mut().take()) { + write(&mut *local); + let mut s = Some(local); + LOCAL_STDERR.with(|slot| { + *slot.borrow_mut() = s.take(); + }); + } else if let Some(mut out) = panic_output() { + write(&mut out); } } @@ -464,7 +461,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, let panics = update_panic_count(1); - // If this is the third nested call (e.g. panics == 2, this is 0-indexed), + // If this is the third nested call (e.g., panics == 2, this is 0-indexed), // the panic hook probably triggered the last panic, otherwise the // double-panic check would have aborted the process. In this case abort the // process real quickly as we don't want to try calling it again as it'll @@ -485,7 +482,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, // Some platforms know that printing to stderr won't ever actually // print anything, and if that's the case we can skip the default // hook. - Hook::Default if stderr_prints_nothing() => {} + Hook::Default if panic_output().is_none() => {} Hook::Default => { info.set_payload(payload.get()); default_hook(&info); @@ -494,7 +491,7 @@ fn rust_panic_with_hook(payload: &mut dyn BoxMeUp, info.set_payload(payload.get()); (*ptr)(&info); } - } + }; HOOK_LOCK.read_unlock(); } diff --git a/src/libstd/path.rs b/src/libstd/path.rs index a153456370c..df05eb7d604 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -109,11 +109,11 @@ use sys::path::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; // Windows Prefixes //////////////////////////////////////////////////////////////////////////////// -/// Windows path prefixes, e.g. `C:` or `\\server\share`. +/// Windows path prefixes, e.g., `C:` or `\\server\share`. /// /// Windows uses a variety of path prefix styles, including references to drive /// volumes (like `C:`), network shared folders (like `\\server\share`), and -/// others. In addition, some path prefixes are "verbatim" (i.e. prefixed with +/// others. In addition, some path prefixes are "verbatim" (i.e., prefixed with /// `\\?\`), in which case `/` is *not* treated as a separator and essentially /// no normalization is performed. /// @@ -148,7 +148,7 @@ use sys::path::{is_sep_byte, is_verbatim_sep, MAIN_SEP_STR, parse_prefix}; #[derive(Copy, Clone, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)] #[stable(feature = "rust1", since = "1.0.0")] pub enum Prefix<'a> { - /// Verbatim prefix, e.g. `\\?\cat_pics`. + /// Verbatim prefix, e.g., `\\?\cat_pics`. /// /// Verbatim prefixes consist of `\\?\` immediately followed by the given /// component. @@ -156,7 +156,7 @@ pub enum Prefix<'a> { Verbatim(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr), /// Verbatim prefix using Windows' _**U**niform **N**aming **C**onvention_, - /// e.g. `\\?\UNC\server\share`. + /// e.g., `\\?\UNC\server\share`. /// /// Verbatim UNC prefixes consist of `\\?\UNC\` immediately followed by the /// server's hostname and a share name. @@ -166,14 +166,14 @@ pub enum Prefix<'a> { #[stable(feature = "rust1", since = "1.0.0")] &'a OsStr, ), - /// Verbatim disk prefix, e.g. `\\?\C:\`. + /// Verbatim disk prefix, e.g., `\\?\C:\`. /// /// Verbatim disk prefixes consist of `\\?\` immediately followed by the /// drive letter and `:\`. #[stable(feature = "rust1", since = "1.0.0")] VerbatimDisk(#[stable(feature = "rust1", since = "1.0.0")] u8), - /// Device namespace prefix, e.g. `\\.\COM42`. + /// Device namespace prefix, e.g., `\\.\COM42`. /// /// Device namespace prefixes consist of `\\.\` immediately followed by the /// device name. @@ -227,7 +227,7 @@ impl<'a> Prefix<'a> { } - /// Determines if the prefix is verbatim, i.e. begins with `\\?\`. + /// Determines if the prefix is verbatim, i.e., begins with `\\?\`. /// /// # Examples /// @@ -509,7 +509,7 @@ impl<'a> Hash for PrefixComponent<'a> { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum Component<'a> { - /// A Windows path prefix, e.g. `C:` or `\\server\share`. + /// A Windows path prefix, e.g., `C:` or `\\server\share`. /// /// There is a large variety of prefix types, see [`Prefix`]'s documentation /// for more. @@ -528,15 +528,15 @@ pub enum Component<'a> { #[stable(feature = "rust1", since = "1.0.0")] RootDir, - /// A reference to the current directory, i.e. `.`. + /// A reference to the current directory, i.e., `.`. #[stable(feature = "rust1", since = "1.0.0")] CurDir, - /// A reference to the parent directory, i.e. `..`. + /// A reference to the parent directory, i.e., `..`. #[stable(feature = "rust1", since = "1.0.0")] ParentDir, - /// A normal component, e.g. `a` and `b` in `a/b`. + /// A normal component, e.g., `a` and `b` in `a/b`. /// /// This variant is the most common one, it represents references to files /// or directories. @@ -615,7 +615,7 @@ pub struct Components<'a> { // true if path *physically* has a root separator; for most Windows // prefixes, it may have a "logical" rootseparator for the purposes of - // normalization, e.g. \\server\share == \\server\share\. + // normalization, e.g., \\server\share == \\server\share\. has_physical_root: bool, // The iterator is double-ended, and these two states keep track of what has @@ -798,7 +798,7 @@ impl<'a> Components<'a> { (comp.len() + extra, self.parse_single_component(comp)) } - // trim away repeated separators (i.e. empty components) on the left + // trim away repeated separators (i.e., empty components) on the left fn trim_left(&mut self) { while !self.path.is_empty() { let (size, comp) = self.parse_next_component(); @@ -810,7 +810,7 @@ impl<'a> Components<'a> { } } - // trim away repeated separators (i.e. empty components) on the right + // trim away repeated separators (i.e., empty components) on the right fn trim_right(&mut self) { while self.path.len() > self.len_before_body() { let (size, comp) = self.parse_next_component_back(); @@ -1178,7 +1178,7 @@ impl PathBuf { /// /// On Windows: /// - /// * if `path` has a root but no prefix (e.g. `\windows`), it + /// * if `path` has a root but no prefix (e.g., `\windows`), it /// replaces everything except for the prefix (if any) of `self`. /// * if `path` has a prefix but no root, it replaces `self`. /// @@ -1225,7 +1225,7 @@ impl PathBuf { if path.is_absolute() || path.prefix().is_some() { self.as_mut_vec().truncate(0); - // `path` has a root but no prefix, e.g. `\windows` (Windows only) + // `path` has a root but no prefix, e.g., `\windows` (Windows only) } else if path.has_root() { let prefix_len = self.components().prefix_remaining(); self.as_mut_vec().truncate(prefix_len); @@ -1397,6 +1397,9 @@ impl<'a> From<&'a Path> for Box<Path> { #[stable(feature = "path_buf_from_box", since = "1.18.0")] impl From<Box<Path>> for PathBuf { + /// Converts a `Box<Path>` into a `PathBuf` + /// + /// This conversion does not allocate or copy memory. fn from(boxed: Box<Path>) -> PathBuf { boxed.into_path_buf() } @@ -1404,6 +1407,10 @@ impl From<Box<Path>> for PathBuf { #[stable(feature = "box_from_path_buf", since = "1.20.0")] impl From<PathBuf> for Box<Path> { + /// Converts a `PathBuf` into a `Box<Path>` + /// + /// This conversion currently should not allocate memory, + /// but this behavior is not guaranteed on all platforms or in all future versions. fn from(p: PathBuf) -> Box<Path> { p.into_boxed_path() } @@ -1426,6 +1433,9 @@ impl<'a, T: ?Sized + AsRef<OsStr>> From<&'a T> for PathBuf { #[stable(feature = "rust1", since = "1.0.0")] impl From<OsString> for PathBuf { + /// Converts a `OsString` into a `PathBuf` + /// + /// This conversion does not allocate or copy memory. fn from(s: OsString) -> PathBuf { PathBuf { inner: s } } @@ -1433,6 +1443,9 @@ impl From<OsString> for PathBuf { #[stable(feature = "from_path_buf_for_os_string", since = "1.14.0")] impl From<PathBuf> for OsString { + /// Converts a `PathBuf` into a `OsString` + /// + /// This conversion does not allocate or copy memory. fn from(path_buf : PathBuf) -> OsString { path_buf.inner } @@ -1440,12 +1453,15 @@ impl From<PathBuf> for OsString { #[stable(feature = "rust1", since = "1.0.0")] impl From<String> for PathBuf { + /// Converts a `String` into a `PathBuf` + /// + /// This conversion does not allocate or copy memory. fn from(s: String) -> PathBuf { PathBuf::from(OsString::from(s)) } } -#[stable(feature = "path_from_str", since = "1.26.0")] +#[stable(feature = "path_from_str", since = "1.32.0")] impl FromStr for PathBuf { type Err = ParseError; @@ -1536,6 +1552,7 @@ impl<'a> From<Cow<'a, Path>> for PathBuf { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<PathBuf> for Arc<Path> { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. #[inline] fn from(s: PathBuf) -> Arc<Path> { let arc: Arc<OsStr> = Arc::from(s.into_os_string()); @@ -1545,6 +1562,7 @@ impl From<PathBuf> for Arc<Path> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl<'a> From<&'a Path> for Arc<Path> { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. #[inline] fn from(s: &Path) -> Arc<Path> { let arc: Arc<OsStr> = Arc::from(s.as_os_str()); @@ -1554,6 +1572,7 @@ impl<'a> From<&'a Path> for Arc<Path> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<PathBuf> for Rc<Path> { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. #[inline] fn from(s: PathBuf) -> Rc<Path> { let rc: Rc<OsStr> = Rc::from(s.into_os_string()); @@ -1563,6 +1582,7 @@ impl From<PathBuf> for Rc<Path> { #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl<'a> From<&'a Path> for Rc<Path> { + /// Converts a Path into a Rc by copying the Path data into a new Rc buffer. #[inline] fn from(s: &Path) -> Rc<Path> { let rc: Rc<OsStr> = Rc::from(s.as_os_str()); @@ -1790,7 +1810,7 @@ impl Path { PathBuf::from(self.inner.to_os_string()) } - /// Returns `true` if the `Path` is absolute, i.e. if it is independent of + /// Returns `true` if the `Path` is absolute, i.e., if it is independent of /// the current directory. /// /// * On Unix, a path is absolute if it starts with the root, so @@ -1819,7 +1839,7 @@ impl Path { } } - /// Returns `true` if the `Path` is relative, i.e. not absolute. + /// Returns `true` if the `Path` is relative, i.e., not absolute. /// /// See [`is_absolute`]'s documentation for more details. /// @@ -1846,9 +1866,9 @@ impl Path { /// * On Unix, a path has a root if it begins with `/`. /// /// * On Windows, a path has a root if it: - /// * has no prefix and begins with a separator, e.g. `\windows` - /// * has a prefix followed by a separator, e.g. `c:\windows` but not `c:windows` - /// * has any non-disk prefix, e.g. `\\server\share` + /// * has no prefix and begins with a separator, e.g., `\windows` + /// * has a prefix followed by a separator, e.g., `c:\windows` but not `c:windows` + /// * has any non-disk prefix, e.g., `\\server\share` /// /// # Examples /// @@ -1960,7 +1980,7 @@ impl Path { /// /// # Errors /// - /// If `base` is not a prefix of `self` (i.e. [`starts_with`] + /// If `base` is not a prefix of `self` (i.e., [`starts_with`] /// returns `false`), returns [`Err`]. /// /// [`starts_with`]: #method.starts_with @@ -2386,7 +2406,7 @@ impl Path { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `false`. /// - /// If you cannot access the directory containing the file, e.g. because of a + /// If you cannot access the directory containing the file, e.g., because of a /// permission error, this will return `false`. /// /// # Examples @@ -2412,7 +2432,7 @@ impl Path { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `false`. /// - /// If you cannot access the directory containing the file, e.g. because of a + /// If you cannot access the directory containing the file, e.g., because of a /// permission error, this will return `false`. /// /// # Examples @@ -2441,7 +2461,7 @@ impl Path { /// This function will traverse symbolic links to query information about the /// destination file. In case of broken symbolic links this will return `false`. /// - /// If you cannot access the directory containing the file, e.g. because of a + /// If you cannot access the directory containing the file, e.g., because of a /// permission error, this will return `false`. /// /// # Examples diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 48acc1096a6..7c1654f62fa 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -426,7 +426,7 @@ mod prim_unit { } /// ## 3. Get it from C. /// /// ``` -/// # #![feature(libc)] +/// # #![feature(rustc_private)] /// extern crate libc; /// /// use std::mem; @@ -462,7 +462,7 @@ mod prim_pointer { } /// /// There are two syntactic forms for creating an array: /// -/// * A list with each element, i.e. `[x, y, z]`. +/// * A list with each element, i.e., `[x, y, z]`. /// * A repeat expression `[x; N]`, which produces an array with `N` copies of `x`. /// The type of `x` must be [`Copy`][copy]. /// diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 2d0848252be..b42ad29034c 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -628,7 +628,7 @@ impl Command { /// /// # Platform-specific behavior /// - /// If the program path is relative (e.g. `"./script.sh"`), it's ambiguous + /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous /// whether it should be interpreted relative to the parent's working /// directory or relative to `current_dir`. The behavior in this case is /// platform specific and unstable, and it's recommended to use diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index 9e957bd87d7..fdaf2a821fa 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -73,18 +73,3 @@ fn lang_start<T: ::process::Termination + 'static> { lang_start_internal(&move || main().report(), argc, argv) } - -/// Function used for reverting changes to the main stack before setrlimit(). -/// This is POSIX (non-Linux) specific and unlikely to be directly stabilized. -#[unstable(feature = "rustc_stack_internals", issue = "0")] -pub unsafe fn deinit_stack_guard() { - ::sys::thread::guard::deinit(); -} - -/// Function used for resetting the main stack guard address after setrlimit(). -/// This is POSIX specific and unlikely to be directly stabilized. -#[unstable(feature = "rustc_stack_internals", issue = "0")] -pub unsafe fn update_stack_guard() { - let main_guard = ::sys::thread::guard::init(); - ::sys_common::thread_info::reset_guard(main_guard); -} diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index a7db372a0e2..81356e68bc0 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -89,7 +89,7 @@ //! //! - A **single processor** executing instructions [out-of-order]: //! Modern CPUs are capable of [superscalar] execution, -//! i.e. multiple instructions might be executing at the same time, +//! i.e., multiple instructions might be executing at the same time, //! even though the machine code describes a sequential process. //! //! This kind of reordering is handled transparently by the CPU. diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs index 90f12c826d6..dc80f561f7a 100644 --- a/src/libstd/sync/mpsc/sync.rs +++ b/src/libstd/sync/mpsc/sync.rs @@ -292,7 +292,7 @@ impl<T> Packet<T> { } } - // NB: Channel could be disconnected while waiting, so the order of + // N.B., channel could be disconnected while waiting, so the order of // these conditionals is important. if guard.disconnected && guard.buf.size() == 0 { return Err(Disconnected); diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index cf9698cb2a9..13e0d2edb83 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -189,7 +189,7 @@ impl Once { /// static INIT: Once = Once::new(); /// /// // Accessing a `static mut` is unsafe much of the time, but if we do so - /// // in a synchronized fashion (e.g. write once or read all) then we're + /// // in a synchronized fashion (e.g., write once or read all) then we're /// // good to go! /// // /// // This function will only call `expensive_computation` once, and will @@ -232,7 +232,7 @@ impl Once { /// Performs the same function as [`call_once`] except ignores poisoning. /// - /// Unlike [`call_once`], if this `Once` has been poisoned (i.e. a previous + /// Unlike [`call_once`], if this `Once` has been poisoned (i.e., a previous /// call to `call_once` or `call_once_force` caused a panic), calling /// `call_once_force` will still invoke the closure `f` and will _not_ /// result in an immediate panic. If `f` panics, the `Once` will remain diff --git a/src/libstd/sys/cloudabi/condvar.rs b/src/libstd/sys/cloudabi/condvar.rs index ccf848a9be4..3229d98624e 100644 --- a/src/libstd/sys/cloudabi/condvar.rs +++ b/src/libstd/sys/cloudabi/condvar.rs @@ -13,7 +13,7 @@ use mem; use sync::atomic::{AtomicU32, Ordering}; use sys::cloudabi::abi; use sys::mutex::{self, Mutex}; -use sys::time::dur2intervals; +use sys::time::checked_dur2intervals; use time::Duration; extern "C" { @@ -114,6 +114,8 @@ impl Condvar { // Call into the kernel to wait on the condition variable. let condvar = self.condvar.get(); + let timeout = checked_dur2intervals(&dur) + .expect("overflow converting duration to nanoseconds"); let subscriptions = [ abi::subscription { type_: abi::eventtype::CONDVAR, @@ -132,7 +134,7 @@ impl Condvar { union: abi::subscription_union { clock: abi::subscription_clock { clock_id: abi::clockid::MONOTONIC, - timeout: dur2intervals(&dur), + timeout, ..mem::zeroed() }, }, diff --git a/src/libstd/sys/cloudabi/shims/env.rs b/src/libstd/sys/cloudabi/shims/env.rs index 31777aa94bc..c7691e3b2df 100644 --- a/src/libstd/sys/cloudabi/shims/env.rs +++ b/src/libstd/sys/cloudabi/shims/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = "cloudabi"; - pub const OS: &'static str = "cloudabi"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "cloudabi"; + pub const OS: &str = "cloudabi"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } diff --git a/src/libstd/sys/cloudabi/shims/net.rs b/src/libstd/sys/cloudabi/shims/net.rs index 93eaf6a9e7d..7229e71d175 100644 --- a/src/libstd/sys/cloudabi/shims/net.rs +++ b/src/libstd/sys/cloudabi/shims/net.rs @@ -13,13 +13,14 @@ use io; use net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use time::Duration; use sys::{unsupported, Void}; +use convert::TryFrom; pub extern crate libc as netc; pub struct TcpStream(Void); impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { unsupported() } @@ -105,7 +106,7 @@ impl fmt::Debug for TcpStream { pub struct TcpListener(Void); impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { unsupported() } @@ -155,7 +156,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(Void); impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { unsupported() } @@ -271,7 +272,7 @@ impl UdpSocket { match self.0 {} } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { match self.0 {} } } @@ -284,6 +285,12 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(Void); +impl LookupHost { + pub fn port(&self) -> u16 { + match self.0 {} + } +} + impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option<SocketAddr> { @@ -291,6 +298,18 @@ impl Iterator for LookupHost { } } -pub fn lookup_host(_: &str) -> io::Result<LookupHost> { - unsupported() +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unsupported() + } } diff --git a/src/libstd/sys/cloudabi/stdio.rs b/src/libstd/sys/cloudabi/stdio.rs index 1d7344f921c..c90dbd8beab 100644 --- a/src/libstd/sys/cloudabi/stdio.rs +++ b/src/libstd/sys/cloudabi/stdio.rs @@ -78,6 +78,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/cloudabi/thread.rs b/src/libstd/sys/cloudabi/thread.rs index 8cca47efd22..a64e0f06849 100644 --- a/src/libstd/sys/cloudabi/thread.rs +++ b/src/libstd/sys/cloudabi/thread.rs @@ -16,7 +16,7 @@ use libc; use mem; use ptr; use sys::cloudabi::abi; -use sys::time::dur2intervals; +use sys::time::checked_dur2intervals; use sys_common::thread::*; use time::Duration; @@ -32,7 +32,8 @@ unsafe impl Send for Thread {} unsafe impl Sync for Thread {} impl Thread { - pub unsafe fn new<'a>(stack: usize, p: Box<dyn FnBox() + 'a>) -> io::Result<Thread> { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); @@ -69,13 +70,15 @@ impl Thread { } pub fn sleep(dur: Duration) { + let timeout = checked_dur2intervals(&dur) + .expect("overflow converting duration to nanoseconds"); unsafe { let subscription = abi::subscription { type_: abi::eventtype::CLOCK, union: abi::subscription_union { clock: abi::subscription_clock { clock_id: abi::clockid::MONOTONIC, - timeout: dur2intervals(&dur), + timeout, ..mem::zeroed() }, }, @@ -118,7 +121,6 @@ pub mod guard { pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { diff --git a/src/libstd/sys/cloudabi/time.rs b/src/libstd/sys/cloudabi/time.rs index a442d1e4ad7..c9fea18fda6 100644 --- a/src/libstd/sys/cloudabi/time.rs +++ b/src/libstd/sys/cloudabi/time.rs @@ -19,15 +19,10 @@ pub struct Instant { t: abi::timestamp, } -fn checked_dur2intervals(dur: &Duration) -> Option<abi::timestamp> { +pub fn checked_dur2intervals(dur: &Duration) -> Option<abi::timestamp> { dur.as_secs() - .checked_mul(NSEC_PER_SEC) - .and_then(|nanos| nanos.checked_add(dur.subsec_nanos() as abi::timestamp)) -} - -pub fn dur2intervals(dur: &Duration) -> abi::timestamp { - checked_dur2intervals(dur) - .expect("overflow converting duration to nanoseconds") + .checked_mul(NSEC_PER_SEC)? + .checked_add(dur.subsec_nanos() as abi::timestamp) } impl Instant { @@ -47,20 +42,16 @@ impl Instant { Duration::new(diff / NSEC_PER_SEC, (diff % NSEC_PER_SEC) as u32) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t - .checked_add(dur2intervals(other)) - .expect("overflow when adding duration to instant"), - } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_add(checked_dur2intervals(other)?)?, + }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t - .checked_sub(dur2intervals(other)) - .expect("overflow when subtracting duration from instant"), - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_sub(checked_dur2intervals(other)?)?, + }) } } @@ -95,23 +86,16 @@ impl SystemTime { } } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - self.checked_add_duration(other) - .expect("overflow when adding duration to instant") - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - checked_dur2intervals(other) - .and_then(|d| self.t.checked_add(d)) - .map(|t| SystemTime {t}) + Some(SystemTime { + t: self.t.checked_add(checked_dur2intervals(other)?)?, + }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { - t: self.t - .checked_sub(dur2intervals(other)) - .expect("overflow when subtracting duration from instant"), - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { + t: self.t.checked_sub(checked_dur2intervals(other)?)?, + }) } } diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index 61e4ce66eec..04c47aeb827 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -48,6 +48,9 @@ cfg_if! { } else if #[cfg(target_arch = "wasm32")] { mod wasm; pub use self::wasm::*; + } else if #[cfg(target_env = "sgx")] { + mod sgx; + pub use self::sgx::*; } else { compile_error!("libstd doesn't compile for this platform yet"); } diff --git a/src/libstd/sys/redox/env.rs b/src/libstd/sys/redox/env.rs index 669b7520df8..75e35046fb7 100644 --- a/src/libstd/sys/redox/env.rs +++ b/src/libstd/sys/redox/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = "redox"; - pub const OS: &'static str = "redox"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "redox"; + pub const OS: &str = "redox"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } diff --git a/src/libstd/sys/redox/net/mod.rs b/src/libstd/sys/redox/net/mod.rs index 67f22231d5f..04a183f2417 100644 --- a/src/libstd/sys/redox/net/mod.rs +++ b/src/libstd/sys/redox/net/mod.rs @@ -9,7 +9,7 @@ // except according to those terms. use fs::File; -use io::{Error, Result, Read}; +use io::{Error, Read, self}; use iter::Iterator; use net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use str::FromStr; @@ -17,6 +17,7 @@ use string::{String, ToString}; use sys::syscall::EINVAL; use time::{self, Duration}; use vec::{IntoIter, Vec}; +use convert::{TryFrom, TryInto}; use self::dns::{Dns, DnsQuery}; @@ -29,7 +30,13 @@ mod dns; mod tcp; mod udp; -pub struct LookupHost(IntoIter<SocketAddr>); +pub struct LookupHost(IntoIter<SocketAddr>, u16); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.1 + } +} impl Iterator for LookupHost { type Item = SocketAddr; @@ -38,65 +45,93 @@ impl Iterator for LookupHost { } } -pub fn lookup_host(host: &str) -> Result<LookupHost> { - let mut ip_string = String::new(); - File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; - let ip: Vec<u8> = ip_string.trim().split('.').map(|part| part.parse::<u8>() - .unwrap_or(0)).collect(); - - let mut dns_string = String::new(); - File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; - let dns: Vec<u8> = dns_string.trim().split('.').map(|part| part.parse::<u8>() - .unwrap_or(0)).collect(); - - if ip.len() == 4 && dns.len() == 4 { - let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); - let tid = (time.subsec_nanos() >> 16) as u16; - - let packet = Dns { - transaction_id: tid, - flags: 0x0100, - queries: vec![DnsQuery { - name: host.to_string(), - q_type: 0x0001, - q_class: 0x0001, - }], - answers: vec![] - }; - - let packet_data = packet.compile(); - - let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); - let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); - let socket = UdpSocket::bind(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0)))?; - socket.set_read_timeout(Some(Duration::new(5, 0)))?; - socket.set_write_timeout(Some(Duration::new(5, 0)))?; - socket.connect(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53)))?; - socket.send(&packet_data)?; - - let mut buf = [0; 65536]; - let count = socket.recv(&mut buf)?; - - match Dns::parse(&buf[.. count]) { - Ok(response) => { - let mut addrs = vec![]; - for answer in response.answers.iter() { - if answer.a_type == 0x0001 && answer.a_class == 0x0001 - && answer.data.len() == 4 - { - let answer_ip = Ipv4Addr::new(answer.data[0], - answer.data[1], - answer.data[2], - answer.data[3]); - addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); - } +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result<LookupHost> { + macro_rules! try_opt { + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(io::Error::new(io::ErrorKind::InvalidInput, + $msg)), } - Ok(LookupHost(addrs.into_iter())) - }, - Err(_err) => Err(Error::from_raw_os_error(EINVAL)) + ) + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> { + let mut ip_string = String::new(); + File::open("/etc/net/ip")?.read_to_string(&mut ip_string)?; + let ip: Vec<u8> = ip_string.trim().split('.').map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + let mut dns_string = String::new(); + File::open("/etc/net/dns")?.read_to_string(&mut dns_string)?; + let dns: Vec<u8> = dns_string.trim().split('.').map(|part| part.parse::<u8>() + .unwrap_or(0)).collect(); + + if ip.len() == 4 && dns.len() == 4 { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap(); + let tid = (time.subsec_nanos() >> 16) as u16; + + let packet = Dns { + transaction_id: tid, + flags: 0x0100, + queries: vec![DnsQuery { + name: host.to_string(), + q_type: 0x0001, + q_class: 0x0001, + }], + answers: vec![] + }; + + let packet_data = packet.compile(); + + let my_ip = Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]); + let dns_ip = Ipv4Addr::new(dns[0], dns[1], dns[2], dns[3]); + let socket = UdpSocket::bind(Ok(&SocketAddr::V4(SocketAddrV4::new(my_ip, 0))))?; + socket.set_read_timeout(Some(Duration::new(5, 0)))?; + socket.set_write_timeout(Some(Duration::new(5, 0)))?; + socket.connect(Ok(&SocketAddr::V4(SocketAddrV4::new(dns_ip, 53))))?; + socket.send(&packet_data)?; + + let mut buf = [0; 65536]; + let count = socket.recv(&mut buf)?; + + match Dns::parse(&buf[.. count]) { + Ok(response) => { + let mut addrs = vec![]; + for answer in response.answers.iter() { + if answer.a_type == 0x0001 && answer.a_class == 0x0001 + && answer.data.len() == 4 + { + let answer_ip = Ipv4Addr::new(answer.data[0], + answer.data[1], + answer.data[2], + answer.data[3]); + addrs.push(SocketAddr::V4(SocketAddrV4::new(answer_ip, 0))); + } + } + Ok(LookupHost(addrs.into_iter(), port)) + }, + Err(_err) => Err(Error::from_raw_os_error(EINVAL)) + } + } else { + Err(Error::from_raw_os_error(EINVAL)) } - } else { - Err(Error::from_raw_os_error(EINVAL)) } } diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs index b5664908479..37457d87f33 100644 --- a/src/libstd/sys/redox/net/tcp.rs +++ b/src/libstd/sys/redox/net/tcp.rs @@ -24,8 +24,8 @@ use super::{path_to_peer_addr, path_to_local_addr}; pub struct TcpStream(File); impl TcpStream { - pub fn connect(addr: &SocketAddr) -> Result<TcpStream> { - let path = format!("tcp:{}", addr); + pub fn connect(addr: Result<&SocketAddr>) -> Result<TcpStream> { + let path = format!("tcp:{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); @@ -180,8 +180,8 @@ impl IntoInner<File> for TcpStream { pub struct TcpListener(File); impl TcpListener { - pub fn bind(addr: &SocketAddr) -> Result<TcpListener> { - let path = format!("tcp:/{}", addr); + pub fn bind(addr: Result<&SocketAddr>) -> Result<TcpListener> { + let path = format!("tcp:/{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs index 22af02079e7..85bfd425924 100644 --- a/src/libstd/sys/redox/net/udp.rs +++ b/src/libstd/sys/redox/net/udp.rs @@ -25,8 +25,8 @@ use super::{path_to_peer_addr, path_to_local_addr}; pub struct UdpSocket(File, UnsafeCell<Option<SocketAddr>>); impl UdpSocket { - pub fn bind(addr: &SocketAddr) -> Result<UdpSocket> { - let path = format!("udp:/{}", addr); + pub fn bind(addr: Result<&SocketAddr>) -> Result<UdpSocket> { + let path = format!("udp:/{}", addr?); let mut options = OpenOptions::new(); options.read(true); options.write(true); @@ -37,8 +37,8 @@ impl UdpSocket { unsafe { &mut *(self.1.get()) } } - pub fn connect(&self, addr: &SocketAddr) -> Result<()> { - unsafe { *self.1.get() = Some(*addr) }; + pub fn connect(&self, addr: Result<&SocketAddr>) -> Result<()> { + unsafe { *self.1.get() = Some(*addr?) }; Ok(()) } diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs index e6a267dd5d9..b1a4ed30404 100644 --- a/src/libstd/sys/redox/path.rs +++ b/src/libstd/sys/redox/path.rs @@ -35,5 +35,5 @@ pub fn parse_prefix(path: &OsStr) -> Option<Prefix> { } } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/redox/process.rs b/src/libstd/sys/redox/process.rs index 4370c1e0502..8f6d83c544a 100644 --- a/src/libstd/sys/redox/process.rs +++ b/src/libstd/sys/redox/process.rs @@ -143,7 +143,7 @@ impl Command { pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + const CLOEXEC_MSG_FOOTER: &[u8] = b"NOEX"; if self.saw_nul { return Err(io::Error::new(ErrorKind::InvalidInput, diff --git a/src/libstd/sys/redox/stdio.rs b/src/libstd/sys/redox/stdio.rs index 7a4d11b0ecb..52cd9334ffb 100644 --- a/src/libstd/sys/redox/stdio.rs +++ b/src/libstd/sys/redox/stdio.rs @@ -76,6 +76,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/redox/thread.rs b/src/libstd/sys/redox/thread.rs index bab91b16e6c..ca014fd576b 100644 --- a/src/libstd/sys/redox/thread.rs +++ b/src/libstd/sys/redox/thread.rs @@ -28,7 +28,8 @@ unsafe impl Send for Thread {} unsafe impl Sync for Thread {} impl Thread { - pub unsafe fn new<'a>(_stack: usize, p: Box<dyn FnBox() + 'a>) -> io::Result<Thread> { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; let id = cvt(syscall::clone(syscall::CLONE_VM | syscall::CLONE_FS | syscall::CLONE_FILES))?; @@ -91,5 +92,4 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs index beff8d287e7..cb2eab52211 100644 --- a/src/libstd/sys/redox/time.rs +++ b/src/libstd/sys/redox/time.rs @@ -41,10 +41,6 @@ impl Timespec { } } - fn add_duration(&self, other: &Duration) -> Timespec { - self.checked_add_duration(other).expect("overflow when adding duration to time") - } - fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() @@ -67,27 +63,25 @@ impl Timespec { }) } - fn sub_duration(&self, other: &Duration) -> Timespec { + fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() .try_into() // <- target type would be `i64` .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs)) - .expect("overflow when subtracting duration from time"); + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1).expect("overflow when subtracting \ - duration from time"); + secs = secs.checked_sub(1)?; } - Timespec { + Some(Timespec { t: syscall::TimeSpec { tv_sec: secs, tv_nsec: nsec as i32, }, - } + }) } } @@ -150,12 +144,12 @@ impl Instant { }) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.add_duration(other) } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub_duration(other)? }) } } @@ -178,16 +172,12 @@ impl SystemTime { self.t.sub_timespec(&other.t) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.add_duration(other) } - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.t.checked_add_duration(other).map(|t| SystemTime { t }) + Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S new file mode 100644 index 00000000000..49ede0674ce --- /dev/null +++ b/src/libstd/sys/sgx/abi/entry.S @@ -0,0 +1,335 @@ +/* Copyright 2018 The Rust Project Developers. See the COPYRIGHT */ +/* file at the top-level directory of this distribution and at */ +/* http://rust-lang.org/COPYRIGHT. */ +/* */ +/* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or */ +/* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license */ +/* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your */ +/* option. This file may not be copied, modified, or distributed */ +/* except according to those terms. */ + +/* This symbol is used at runtime to figure out the virtual address that the */ +/* enclave is loaded at. */ +.section absolute +.global IMAGE_BASE +IMAGE_BASE: + +.section .rodata +/* The XSAVE area needs to be a large chunk of readable memory, but since we are */ +/* going to restore everything to its initial state (XSTATE_BV=0), only certain */ +/* parts need to have a defined value. In particular: */ +/* */ +/* * MXCSR in the legacy area. This register is always restored if RFBM[1] or */ +/* RFBM[2] is set, regardless of the value of XSTATE_BV */ +/* * XSAVE header */ +.align 64 +.Lxsave_clear: +.org .+24 +.Lxsave_mxcsr: + .int 0 + +/* We can store a bunch of data in the gap between MXCSR and the XSAVE header */ + +/* The following symbols point at read-only data that will be filled in by the */ +/* post-linker. */ + +/* When using this macro, don't forget to adjust the linker version script! */ +.macro globvar name:req size:req + .global \name + .protected \name + .align \size + .size \name , \size + \name : + .org .+\size +.endm + /* The base address (relative to enclave start) of the heap area */ + globvar HEAP_BASE 8 + /* The heap size in bytes */ + globvar HEAP_SIZE 8 + /* Value of the RELA entry in the dynamic table */ + globvar RELA 8 + /* Value of the RELACOUNT entry in the dynamic table */ + globvar RELACOUNT 8 + /* The enclave size in bytes */ + globvar ENCLAVE_SIZE 8 + /* The base address (relative to enclave start) of the enclave configuration area */ + 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!" +.Lreentry_panic_msg_end: + +.Lusercall_panic_msg: + .asciz "Invalid usercall#!" +.Lusercall_panic_msg_end: + +.org .Lxsave_clear+512 +.Lxsave_header: + .int 0, 0 /* XSTATE_BV */ + .int 0, 0 /* XCOMP_BV */ + .org .+48 /* reserved bits */ + +.data +.Lpanicked: + .byte 0 + +/* TCS local storage section */ +.equ tcsls_tos, 0x00 /* initialized by loader to *offset* from image base to TOS */ +.equ tcsls_flags, 0x08 /* initialized by loader */ +.equ tcsls_flag_secondary, 0 /* initialized by loader; 0 = standard TCS, 1 = secondary TCS */ +.equ tcsls_flag_init_once, 1 /* initialized by loader to 0 */ +/* 14 unused bits */ +.equ tcsls_user_fcw, 0x0a +.equ tcsls_user_mxcsr, 0x0c +.equ tcsls_last_rsp, 0x10 /* initialized by loader to 0 */ +.equ tcsls_panic_last_rsp, 0x18 /* initialized by loader to 0 */ +.equ tcsls_debug_panic_buf_ptr, 0x20 /* initialized by loader to 0 */ +.equ tcsls_user_rsp, 0x28 +.equ tcsls_user_retip, 0x30 +.equ tcsls_user_rbp, 0x38 +.equ tcsls_user_r12, 0x40 +.equ tcsls_user_r13, 0x48 +.equ tcsls_user_r14, 0x50 +.equ tcsls_user_r15, 0x58 +.equ tcsls_tls_ptr, 0x60 +.equ tcsls_tcs_addr, 0x68 + +.macro load_tcsls_flag_secondary_bool reg:req comments:vararg + .ifne tcsls_flag_secondary /* to convert to a bool, must be the first bit */ + .abort + .endif + mov $(1<<tcsls_flag_secondary),%e\reg + and %gs:tcsls_flags,%\reg +.endm + +.text +.global sgx_entry +.type sgx_entry,function +sgx_entry: +/* save user registers */ + mov %rcx,%gs:tcsls_user_retip + mov %rsp,%gs:tcsls_user_rsp + mov %rbp,%gs:tcsls_user_rbp + mov %r12,%gs:tcsls_user_r12 + mov %r13,%gs:tcsls_user_r13 + mov %r14,%gs:tcsls_user_r14 + mov %r15,%gs:tcsls_user_r15 + mov %rbx,%gs:tcsls_tcs_addr + stmxcsr %gs:tcsls_user_mxcsr + fnstcw %gs:tcsls_user_fcw +/* reset user state */ + cld /* x86-64 ABI requires DF to be unset at function entry/exit */ +/* check for debug buffer pointer */ + testb $0xff,DEBUG(%rip) + jz .Lskip_debug_init + mov %r10,%gs:tcsls_debug_panic_buf_ptr +.Lskip_debug_init: +/* check if returning from usercall */ + mov %gs:tcsls_last_rsp,%r11 + test %r11,%r11 + jnz .Lusercall_ret +/* setup stack */ + mov %gs:tcsls_tos,%rsp /* initially, RSP is not set to the correct value */ + /* here. This is fixed below under "adjust stack". */ +/* check for thread init */ + bts $tcsls_flag_init_once,%gs:tcsls_flags + jc .Lskip_init +/* adjust stack */ + lea IMAGE_BASE(%rip),%rax + add %rax,%rsp + mov %rsp,%gs:tcsls_tos +/* call tcs_init */ +/* store caller-saved registers in callee-saved registers */ + mov %rdi,%rbx + mov %rsi,%r12 + mov %rdx,%r13 + mov %r8,%r14 + mov %r9,%r15 + load_tcsls_flag_secondary_bool di /* RDI = tcs_init() argument: secondary: bool */ + call tcs_init +/* reload caller-saved registers */ + mov %rbx,%rdi + mov %r12,%rsi + mov %r13,%rdx + mov %r14,%r8 + mov %r15,%r9 +.Lskip_init: +/* check for panic */ + bt $0,.Lpanicked(%rip) + jc .Lreentry_panic +/* call into main entry point */ + load_tcsls_flag_secondary_bool cx /* RCX = entry() argument: secondary: bool */ + call entry /* RDI, RSI, RDX, R8, R9 passed in from userspace */ + mov %rax,%rsi /* RSI = return value */ + /* NOP: mov %rdx,%rdx */ /* RDX = return value */ + xor %rdi,%rdi /* RDI = normal exit */ +.Lexit: +/* clear general purpose register state */ + /* RAX overwritten by ENCLU */ + /* RBX set later */ + /* RCX overwritten by ENCLU */ + /* RDX contains return value */ + /* RSP set later */ + /* RBP set later */ + /* RDI contains exit mode */ + /* RSI contains return value */ + xor %r8,%r8 + xor %r9,%r9 + xor %r10,%r10 + xor %r11,%r11 + /* R12 ~ R15 set by sgx_exit */ +.Lsgx_exit: +/* clear extended register state */ + mov %rdx, %rcx /* save RDX */ + mov $-1, %rax + mov %rax, %rdx + xrstor .Lxsave_clear(%rip) + mov %rcx, %rdx /* restore RDX */ +/* clear flags */ + pushq $0 + popfq +/* restore user registers */ + mov %gs:tcsls_user_r12,%r12 + mov %gs:tcsls_user_r13,%r13 + mov %gs:tcsls_user_r14,%r14 + mov %gs:tcsls_user_r15,%r15 + mov %gs:tcsls_user_retip,%rbx + mov %gs:tcsls_user_rsp,%rsp + mov %gs:tcsls_user_rbp,%rbp + fldcw %gs:tcsls_user_fcw + ldmxcsr %gs:tcsls_user_mxcsr +/* exit enclave */ + mov $0x4,%eax /* EEXIT */ + enclu +/* end sgx_entry */ + +.Lreentry_panic: + lea .Lreentry_panic_msg(%rip),%rdi + mov $.Lreentry_panic_msg_end-.Lreentry_panic_msg,%esi + orq $8,%rsp + jmp panic_msg + +.Lusercall_panic: + lea .Lusercall_panic_msg(%rip),%rdi + mov $.Lusercall_panic_msg_end-.Lusercall_panic_msg,%esi + orq $8,%rsp + jmp panic_msg + +.macro push_callee_saved_registers + push %r15 + push %r14 + push %r13 + push %r12 + push %rbp + push %rbx + sub $8, %rsp + fstcw 4(%rsp) + stmxcsr (%rsp) +.endm + +.global panic_exit +panic_exit: +/* save registers in DEBUG mode, so that debugger can reconstruct the stack */ + testb $0xff,DEBUG(%rip) + jz .Lskip_save_registers + push_callee_saved_registers + movq %rsp,%gs:tcsls_panic_last_rsp +.Lskip_save_registers: +/* set panicked bit */ + movb $1,.Lpanicked(%rip) +/* call usercall exit(true) */ + mov $1,%esi /* RSI = usercall() argument: panic = true */ + xor %rdx,%rdx /* RDX cleared */ + movq $usercall_nr_exit,%rdi /* RDI = usercall exit */ + jmp .Lexit + +/* This *MUST* be called with 6 parameters, otherwise register information */ +/* might leak! */ +.global usercall +usercall: + test %rdi,%rdi + jle .Lusercall_panic +/* save callee-saved state */ + push_callee_saved_registers + movq %rsp,%gs:tcsls_last_rsp +/* clear general purpose register state */ + /* RAX overwritten by ENCLU */ + /* RBX set by sgx_exit */ + /* RCX overwritten by ENCLU */ + /* RDX contains parameter */ + /* RSP set by sgx_exit */ + /* RBP set by sgx_exit */ + /* RDI contains parameter */ + /* RSI contains parameter */ + /* R8 contains parameter */ + /* R9 contains parameter */ + xor %r10,%r10 + xor %r11,%r11 + /* R12 ~ R15 set by sgx_exit */ +/* extended registers/flags cleared by sgx_exit */ +/* exit */ + jmp .Lsgx_exit +.Lusercall_ret: + movq $0,%gs:tcsls_last_rsp +/* restore callee-saved state, cf. push_callee_saved_registers */ + mov %r11,%rsp + ldmxcsr (%rsp) + fldcw 4(%rsp) + add $8, %rsp + pop %rbx + pop %rbp + pop %r12 + pop %r13 + pop %r14 + pop %r15 +/* return */ + mov %rsi,%rax /* RAX = return value */ + /* NOP: mov %rdx,%rdx */ /* RDX = return value */ + ret + +/* +The following functions need to be defined externally: +``` +// Called by entry code when it needs to panic +extern "C" fn panic_msg(msg: &'static str) -> ! { + panic!(msg) +} + +// Called once when a TCS is first entered +extern "C" fn tcs_init(secondary: bool); + +// Standard TCS entrypoint +extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64); +``` +*/ + +.global get_tcs_addr +get_tcs_addr: + mov %gs:tcsls_tcs_addr,%rax + ret + +.global get_tls_ptr +get_tls_ptr: + mov %gs:tcsls_tls_ptr,%rax + ret + +.global set_tls_ptr +set_tls_ptr: + mov %rdi,%gs:tcsls_tls_ptr + ret + +.global take_debug_panic_buf_ptr +take_debug_panic_buf_ptr: + xor %rax,%rax + xchg %gs:tcsls_debug_panic_buf_ptr,%rax + ret diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs new file mode 100644 index 00000000000..bf32c712216 --- /dev/null +++ b/src/libstd/sys/sgx/abi/mem.rs @@ -0,0 +1,42 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Do not remove inline: will result in relocation failure +#[inline(always)] +pub unsafe fn rel_ptr<T>(offset: u64) -> *const T { + (image_base() + offset) as *const T +} + +// Do not remove inline: will result in relocation failure +#[inline(always)] +pub unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T { + (image_base() + offset) as *mut T +} + +extern { + static ENCLAVE_SIZE: usize; +} + +// 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 +#[inline(always)] +fn image_base() -> u64 { + let base; + unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) }; + base +} + +pub fn is_user_range(p: *const u8, len: usize) -> bool { + let start=p as u64; + let end=start + (len as u64); + end <= image_base() || + start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant +} diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs new file mode 100644 index 00000000000..069cca3b98e --- /dev/null +++ b/src/libstd/sys/sgx/abi/mod.rs @@ -0,0 +1,99 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::sync::atomic::{AtomicUsize, Ordering}; +use io::Write; + +// runtime features +mod reloc; +mod mem; +pub(super) mod panic; + +// library features +pub mod thread; +pub mod tls; +#[macro_use] +pub mod usercalls; + +global_asm!(concat!(usercalls_asm!(), include_str!("entry.S"))); + +#[no_mangle] +unsafe extern "C" fn tcs_init(secondary: bool) { + // Be very careful when changing this code: it runs before the binary has been + // relocated. Any indirect accesses to symbols will likely fail. + const UNINIT: usize = 0; + const BUSY: usize = 1; + const DONE: usize = 2; + // Three-state spin-lock + static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT); + + if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE { + panic::panic_msg("Entered secondary TCS before main TCS!") + } + + // Try to atomically swap UNINIT with BUSY. The returned state can be: + match RELOC_STATE.compare_and_swap(UNINIT, BUSY, Ordering::Acquire) { + // This thread just obtained the lock and other threads will observe BUSY + UNINIT => { + reloc::relocate_elf_rela(); + RELOC_STATE.store(DONE, Ordering::Release); + }, + // We need to wait until the initialization is done. + BUSY => while RELOC_STATE.load(Ordering::Acquire) == BUSY { + ::core::arch::x86_64::_mm_pause() + }, + // Initialization is done. + DONE => {}, + _ => unreachable!() + } +} + +// FIXME: this item should only exist if this is linked into an executable +// (main function exists). If this is a library, the crate author should be +// able to specify this +#[no_mangle] +extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) { + // FIXME: how to support TLS in library mode? + let tls = Box::new(tls::Tls::new()); + let _tls_guard = unsafe { tls.activate() }; + + if secondary { + super::thread::Thread::entry(); + + (0, 0) + } else { + extern "C" { + fn main(argc: isize, argv: *const *const u8) -> isize; + } + + // check entry is being called according to ABI + assert_eq!(p3, 0); + assert_eq!(p4, 0); + assert_eq!(p5, 0); + + unsafe { + // The actual types of these arguments are `p1: *const Arg, p2: + // usize`. We can't currently customize the argument list of Rust's + // main function, so we pass these in as the standard pointer-sized + // values in `argc` and `argv`. + let ret = main(p2 as _, p1 as _); + exit_with_code(ret) + } + } +} + +pub(super) fn exit_with_code(code: isize) -> ! { + if code != 0 { + if let Some(mut out) = panic::SgxPanicOutput::new() { + let _ = write!(out, "Exited with status code {}", code); + } + } + usercalls::exit(code != 0); +} diff --git a/src/libstd/sys/sgx/abi/panic.rs b/src/libstd/sys/sgx/abi/panic.rs new file mode 100644 index 00000000000..dd9159b9fe2 --- /dev/null +++ b/src/libstd/sys/sgx/abi/panic.rs @@ -0,0 +1,58 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io::{self, Write}; +use slice::from_raw_parts_mut; + +extern "C" { + fn take_debug_panic_buf_ptr() -> *mut u8; + static DEBUG: u8; +} + +pub(crate) struct SgxPanicOutput(Option<&'static mut [u8]>); + +impl SgxPanicOutput { + pub(crate) fn new() -> Option<Self> { + if unsafe { DEBUG == 0 } { + None + } else { + Some(SgxPanicOutput(None)) + } + } + + fn init(&mut self) -> &mut &'static mut [u8] { + self.0.get_or_insert_with(|| unsafe { + let ptr = take_debug_panic_buf_ptr(); + if ptr.is_null() { + &mut [] + } else { + from_raw_parts_mut(ptr, 1024) + } + }) + } +} + +impl Write for SgxPanicOutput { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.init().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.init().flush() + } +} + +#[no_mangle] +pub extern "C" fn panic_msg(msg: &str) -> ! { + let _ = SgxPanicOutput::new().map(|mut out| out.write(msg.as_bytes())); + unsafe { panic_exit(); } +} + +extern "C" { pub fn panic_exit() -> !; } diff --git a/src/libstd/sys/sgx/abi/reloc.rs b/src/libstd/sys/sgx/abi/reloc.rs new file mode 100644 index 00000000000..2d5e14d6ad1 --- /dev/null +++ b/src/libstd/sys/sgx/abi/reloc.rs @@ -0,0 +1,40 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use slice::from_raw_parts; +use super::mem; + +const R_X86_64_RELATIVE: u32 = 8; + +#[repr(packed)] +struct Rela<T> { + offset: T, + info: T, + addend: T, +} + +pub fn relocate_elf_rela() { + extern { + static RELA: u64; + static RELACOUNT: usize; + } + + if unsafe { RELACOUNT } == 0 { return } // unsafe ok: link-time constant + + let relas = unsafe { + from_raw_parts::<Rela<u64>>(mem::rel_ptr(RELA), RELACOUNT) // unsafe ok: link-time constant + }; + for rela in relas { + if rela.info != (/*0 << 32 |*/ R_X86_64_RELATIVE as u64) { + panic!("Invalid relocation"); + } + unsafe { *mem::rel_ptr_mut::<*const ()>(rela.offset) = mem::rel_ptr(rela.addend) }; + } +} diff --git a/src/libstd/sys/sgx/abi/thread.rs b/src/libstd/sys/sgx/abi/thread.rs new file mode 100644 index 00000000000..4640b812fea --- /dev/null +++ b/src/libstd/sys/sgx/abi/thread.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::Tcs; + +/// Get the ID for the current thread. The ID is guaranteed to be unique among +/// all currently running threads in the enclave, and it is guaranteed to be +/// constant for the lifetime of the thread. More specifically for SGX, there +/// is a one-to-one correspondence of the ID to the address of the TCS. +pub fn current() -> Tcs { + extern "C" { fn get_tcs_addr() -> Tcs; } + unsafe { get_tcs_addr() } +} diff --git a/src/libstd/sys/sgx/abi/tls.rs b/src/libstd/sys/sgx/abi/tls.rs new file mode 100644 index 00000000000..ab7822182a5 --- /dev/null +++ b/src/libstd/sys/sgx/abi/tls.rs @@ -0,0 +1,246 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +use ptr; +use mem; +use cell::Cell; +use num::NonZeroUsize; +use self::sync_bitset::*; + +#[cfg(target_pointer_width="64")] +const USIZE_BITS: usize = 64; +const TLS_KEYS: usize = 128; // Same as POSIX minimum +const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; + +static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; +macro_rules! dup { + ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); + (() $($val:tt)*) => ([$($val),*]) +} +static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) ATOMIC_USIZE_INIT); + +extern "C" { + fn get_tls_ptr() -> *const u8; + fn set_tls_ptr(tls: *const u8); +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Key(NonZeroUsize); + +impl Key { + fn to_index(self) -> usize { + self.0.get() - 1 + } + + fn from_index(index: usize) -> Self { + Key(NonZeroUsize::new(index + 1).unwrap()) + } + + pub fn as_usize(self) -> usize { + self.0.get() + } + + pub fn from_usize(index: usize) -> Self { + Key(NonZeroUsize::new(index).unwrap()) + } +} + +#[repr(C)] +pub struct Tls { + data: [Cell<*mut u8>; TLS_KEYS] +} + +pub struct ActiveTls<'a> { + tls: &'a Tls +} + +impl<'a> Drop for ActiveTls<'a> { + fn drop(&mut self) { + let value_with_destructor = |key: usize| { + let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed); + unsafe { mem::transmute::<_,Option<unsafe extern fn(*mut u8)>>(ptr) } + .map(|dtor| (&self.tls.data[key], dtor)) + }; + + let mut any_non_null_dtor = true; + while any_non_null_dtor { + any_non_null_dtor = false; + for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) { + let value = value.replace(ptr::null_mut()); + if value != ptr::null_mut() { + any_non_null_dtor = true; + unsafe { dtor(value) } + } + } + } + } +} + +impl Tls { + pub fn new() -> Tls { + Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } + } + + pub unsafe fn activate(&self) -> ActiveTls { + set_tls_ptr(self as *const Tls as _); + ActiveTls { tls: self } + } + + #[allow(unused)] + pub unsafe fn activate_persistent(self: Box<Self>) { + set_tls_ptr((&*self) as *const Tls as _); + mem::forget(self); + } + + unsafe fn current<'a>() -> &'a Tls { + &*(get_tls_ptr() as *const Tls) + } + + pub fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded"); + TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); + Key::from_index(index) + } + + pub fn set(key: Key, value: *mut u8) { + let index = key.to_index(); + assert!(TLS_KEY_IN_USE.get(index)); + unsafe { Self::current() }.data[index].set(value); + } + + pub fn get(key: Key) -> *mut u8 { + let index = key.to_index(); + assert!(TLS_KEY_IN_USE.get(index)); + unsafe { Self::current() }.data[index].get() + } + + pub fn destroy(key: Key) { + TLS_KEY_IN_USE.clear(key.to_index()); + } +} + +mod sync_bitset { + use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + use iter::{Enumerate, Peekable}; + use slice::Iter; + use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS}; + + /// A bitset that can be used synchronously. + pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); + + pub(super) const SYNC_BITSET_INIT: SyncBitset = + SyncBitset([ATOMIC_USIZE_INIT, ATOMIC_USIZE_INIT]); + + impl SyncBitset { + pub fn get(&self, index: usize) -> bool { + let (hi, lo) = Self::split(index); + (self.0[hi].load(Ordering::Relaxed) & lo) != 0 + } + + /// Not atomic. + pub fn iter(&self) -> SyncBitsetIter { + SyncBitsetIter { + iter: self.0.iter().enumerate().peekable(), + elem_idx: 0, + } + } + + pub fn clear(&self, index: usize) { + let (hi, lo) = Self::split(index); + self.0[hi].fetch_and(!lo, Ordering::Relaxed); + } + + /// Set any unset bit. Not atomic. Returns `None` if all bits were + /// observed to be set. + pub fn set(&self) -> Option<usize> { + 'elems: for (idx, elem) in self.0.iter().enumerate() { + let mut current = elem.load(Ordering::Relaxed); + loop { + if 0 == !current { + continue 'elems; + } + let trailing_ones = (!current).trailing_zeros() as usize; + match elem.compare_exchange( + current, + current | (1 << trailing_ones), + Ordering::AcqRel, + Ordering::Relaxed + ) { + Ok(_) => return Some(idx * USIZE_BITS + trailing_ones), + Err(previous) => current = previous, + } + } + } + None + } + + fn split(index: usize) -> (usize, usize) { + (index / USIZE_BITS, 1 << (index % USIZE_BITS)) + } + } + + pub(super) struct SyncBitsetIter<'a> { + iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>, + elem_idx: usize, + } + + impl<'a> Iterator for SyncBitsetIter<'a> { + type Item = usize; + + fn next(&mut self) -> Option<usize> { + self.iter.peek().cloned().and_then(|(idx, elem)| { + let elem = elem.load(Ordering::Relaxed); + let low_mask = (1 << self.elem_idx) - 1; + let next = elem & !low_mask; + let next_idx = next.trailing_zeros() as usize; + self.elem_idx = next_idx + 1; + if self.elem_idx >= 64 { + self.elem_idx = 0; + self.iter.next(); + } + match next_idx { + 64 => self.next(), + _ => Some(idx * USIZE_BITS + next_idx), + } + }) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + fn test_data(bitset: [usize; 2], bit_indices: &[usize]) { + let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]); + assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices); + for &i in bit_indices { + assert!(set.get(i)); + } + } + + #[test] + fn iter() { + test_data([0b0110_1001, 0], &[0, 3, 5, 6]); + test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]); + test_data([0, 0], &[]); + } + + #[test] + fn set_get_clear() { + let set = SYNC_BITSET_INIT; + let key = set.set().unwrap(); + assert!(set.get(key)); + set.clear(key); + assert!(!set.get(key)); + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs new file mode 100644 index 00000000000..64968a9970c --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs @@ -0,0 +1,404 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +use ptr; +use mem; +use cell::UnsafeCell; +use slice; +use ops::{Deref, DerefMut, Index, IndexMut, CoerceUnsized}; +use slice::SliceIndex; + +use fortanix_sgx_abi::*; +use super::super::mem::is_user_range; + +/// A type that can be safely read from or written to userspace. +/// +/// Non-exhaustive list of specific requirements for reading and writing: +/// * **Type is `Copy`** (and therefore also not `Drop`). Copies will be +/// created when copying from/to userspace. Destructors will not be called. +/// * **No references or Rust-style owned pointers** (`Vec`, `Arc`, etc.). When +/// reading from userspace, references into enclave memory must not be +/// created. Also, only enclave memory is considered managed by the Rust +/// compiler's static analysis. When reading from userspace, there can be no +/// guarantee that the value correctly adheres to the expectations of the +/// type. When writing to userspace, memory addresses of data in enclave +/// memory must not be leaked for confidentiality reasons. `User` and +/// `UserRef` are also not allowed for the same reasons. +/// * **No fat pointers.** When reading from userspace, the size or vtable +/// pointer could be automatically interpreted and used by the code. When +/// writing to userspace, memory addresses of data in enclave memory (such +/// as vtable pointers) must not be leaked for confidentiality reasons. +/// +/// Non-exhaustive list of specific requirements for reading from userspace: +/// * Any bit pattern is valid for this type (no `enum`s). There can be no +/// guarantee that the value correctly adheres to the expectations of the +/// type, so any value must be valid for this type. +/// +/// Non-exhaustive list of specific requirements for writing to userspace: +/// * No pointers to enclave memory. Memory addresses of data in enclave memory +/// must not be leaked for confidentiality reasons. +/// * No internal padding. Padding might contain previously-initialized secret +/// data stored at that memory location and must not be leaked for +/// confidentiality reasons. +pub unsafe trait UserSafeSized: Copy + Sized {} + +unsafe impl UserSafeSized for u8 {} +unsafe impl<T> UserSafeSized for FifoDescriptor<T> {} +unsafe impl UserSafeSized for ByteBuffer {} +unsafe impl UserSafeSized for Usercall {} +unsafe impl UserSafeSized for Return {} +unsafe impl<T: UserSafeSized> UserSafeSized for [T; 2] {} + +/// A type that can be represented in memory as one or more `UserSafeSized`s. +pub unsafe trait UserSafe { + unsafe fn align_of() -> usize; + + /// NB. This takes a size, not a length! + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self; + + /// NB. This takes a size, not a length! + unsafe fn from_raw_sized(ptr: *const u8, size: usize) -> *const Self { + let ret = Self::from_raw_sized_unchecked(ptr, size); + Self::check_ptr(ret); + ret + } + + unsafe fn check_ptr(ptr: *const Self) { + let is_aligned = |p| -> bool { + 0 == (p as usize) & (Self::align_of() - 1) + }; + + assert!(is_aligned(ptr as *const u8)); + assert!(is_user_range(ptr as _, mem::size_of_val(&*ptr))); + assert!(!ptr.is_null()); + } +} + +unsafe impl<T: UserSafeSized> UserSafe for T { + unsafe fn align_of() -> usize { + mem::align_of::<T>() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + assert_eq!(size, mem::size_of::<T>()); + ptr as _ + } +} + +unsafe impl<T: UserSafeSized> UserSafe for [T] { + unsafe fn align_of() -> usize { + mem::align_of::<T>() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + let elem_size = mem::size_of::<T>(); + assert_eq!(size % elem_size, 0); + let len = size / elem_size; + slice::from_raw_parts(ptr as _, len) + } +} + +/// A reference to some type in userspace memory. `&UserRef<T>` is equivalent +/// to `&T` in enclave memory. Access to the memory is only allowed by copying +/// to avoid TOCTTOU issues. After copying, code should make sure to completely +/// check the value before use. +pub struct UserRef<T: ?Sized>(UnsafeCell<T>); +/// An owned type in userspace memory. `User<T>` is equivalent to `Box<T>` in +/// enclave memory. Access to the memory is only allowed by copying to avoid +/// TOCTTOU issues. The user memory will be freed when the value is dropped. +/// After copying, code should make sure to completely check the value before +/// use. +pub struct User<T: UserSafe + ?Sized>(*mut UserRef<T>); + +impl<T: ?Sized> User<T> where T: UserSafe { + // This function returns memory that is practically uninitialized, but is + // not considered "unspecified" or "undefined" for purposes of an + // optimizing compiler. This is achieved by returning a pointer from + // from outside as obtained by `super::alloc`. + fn new_uninit_bytes(size: usize) -> Self { + unsafe { + let ptr = super::alloc(size, T::align_of()).expect("User memory allocation failed"); + User(T::from_raw_sized(ptr as _, size) as _) + } + } + + pub fn new_from_enclave(val: &T) -> Self { + unsafe { + let ret = Self::new_uninit_bytes(mem::size_of_val(val)); + ptr::copy( + val as *const T as *const u8, + ret.0 as *mut T as *mut u8, + mem::size_of_val(val) + ); + ret + } + } + + /// Create an owned `User<T>` from a raw pointer. The pointer should be + /// freeable with the `free` usercall and the alignment of `T`. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw(ptr: *mut T) -> Self { + T::check_ptr(ptr); + User(ptr as _) + } + + /// Convert this value into a raw pointer. The value will no longer be + /// automatically freed. + pub fn into_raw(self) -> *mut T { + let ret = self.0; + mem::forget(self); + ret as _ + } +} + +impl<T> User<T> where T: UserSafe { + pub fn uninitialized() -> Self { + Self::new_uninit_bytes(mem::size_of::<T>()) + } +} + +impl<T> User<[T]> where [T]: UserSafe { + pub fn uninitialized(n: usize) -> Self { + Self::new_uninit_bytes(n * mem::size_of::<T>()) + } + + /// Create an owned `User<[T]>` from a raw thin pointer and a slice length. + /// The pointer should be freeable with the `free` usercall and the + /// alignment of `T`. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { + User(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as _) + } +} + +impl<T: ?Sized> UserRef<T> where T: UserSafe { + /// Create a `&UserRef<[T]>` from a raw pointer. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self { + T::check_ptr(ptr); + &*(ptr as *const Self) + } + + /// Create a `&mut UserRef<[T]>` from a raw pointer. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self { + T::check_ptr(ptr); + &mut*(ptr as *mut Self) + } + + /// # Panics + /// This function panics if the destination doesn't have the same size as + /// the source. This can happen for dynamically-sized types such as slices. + pub fn copy_from_enclave(&mut self, val: &T) { + unsafe { + assert_eq!(mem::size_of_val(val), mem::size_of_val( &*self.0.get() )); + ptr::copy( + val as *const T as *const u8, + self.0.get() as *mut T as *mut u8, + mem::size_of_val(val) + ); + } + } + + /// # Panics + /// This function panics if the destination doesn't have the same size as + /// the source. This can happen for dynamically-sized types such as slices. + pub fn copy_to_enclave(&self, dest: &mut T) { + unsafe { + assert_eq!(mem::size_of_val(dest), mem::size_of_val( &*self.0.get() )); + ptr::copy( + self.0.get() as *const T as *const u8, + dest as *mut T as *mut u8, + mem::size_of_val(dest) + ); + } + } + + pub fn as_raw_ptr(&self) -> *const T { + self as *const _ as _ + } + + pub fn as_raw_mut_ptr(&mut self) -> *mut T { + self as *mut _ as _ + } +} + +impl<T> UserRef<T> where T: UserSafe { + pub fn to_enclave(&self) -> T { + unsafe { ptr::read(self.0.get()) } + } +} + +impl<T> UserRef<[T]> where [T]: UserSafe { + /// Create a `&UserRef<[T]>` from a raw thin pointer and a slice length. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self { + &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *const Self) + } + + /// Create a `&mut UserRef<[T]>` from a raw thin pointer and a slice length. + /// + /// # Panics + /// This function panics if: + /// + /// * The pointer is not aligned + /// * The pointer is null + /// * The pointed-to range is not in user memory + pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self { + &mut*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *mut Self) + } + + pub fn as_ptr(&self) -> *const T { + self.0.get() as _ + } + + pub fn as_mut_ptr(&mut self) -> *mut T { + self.0.get() as _ + } + + pub fn len(&self) -> usize { + unsafe { (*self.0.get()).len() } + } + + pub fn copy_to_enclave_vec(&self, dest: &mut Vec<T>) { + unsafe { + if let Some(missing) = self.len().checked_sub(dest.capacity()) { + dest.reserve(missing) + } + dest.set_len(self.len()); + self.copy_to_enclave(&mut dest[..]); + } + } + + pub fn to_enclave(&self) -> Vec<T> { + let mut ret = Vec::with_capacity(self.len()); + self.copy_to_enclave_vec(&mut ret); + ret + } + + pub fn iter(&self) -> Iter<T> + where T: UserSafe // FIXME: should be implied by [T]: UserSafe? + { + unsafe { + Iter((&*self.as_raw_ptr()).iter()) + } + } + + pub fn iter_mut(&mut self) -> IterMut<T> + where T: UserSafe // FIXME: should be implied by [T]: UserSafe? + { + unsafe { + IterMut((&mut*self.as_raw_mut_ptr()).iter_mut()) + } + } +} + +pub struct Iter<'a, T: 'a + UserSafe>(slice::Iter<'a, T>); + +impl<'a, T: UserSafe> Iterator for Iter<'a, T> { + type Item = &'a UserRef<T>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + unsafe { + self.0.next().map(|e| UserRef::from_ptr(e)) + } + } +} + +pub struct IterMut<'a, T: 'a + UserSafe>(slice::IterMut<'a, T>); + +impl<'a, T: UserSafe> Iterator for IterMut<'a, T> { + type Item = &'a mut UserRef<T>; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + unsafe { + self.0.next().map(|e| UserRef::from_mut_ptr(e)) + } + } +} + +impl<T: ?Sized> Deref for User<T> where T: UserSafe { + type Target = UserRef<T>; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} + +impl<T: ?Sized> DerefMut for User<T> where T: UserSafe { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut*self.0 } + } +} + +impl<T: ?Sized> Drop for User<T> where T: UserSafe { + fn drop(&mut self) { + unsafe { + let ptr = (*self.0).0.get(); + super::free(ptr as _, mem::size_of_val(&mut*ptr), T::align_of()); + } + } +} + +impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {} + +impl<T, I: SliceIndex<[T]>> Index<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + type Output = UserRef<I::Output>; + + #[inline] + fn index(&self, index: I) -> &UserRef<I::Output> { + unsafe { + UserRef::from_ptr(index.index(&*self.as_raw_ptr())) + } + } +} + +impl<T, I: SliceIndex<[T]>> IndexMut<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + #[inline] + fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> { + unsafe { + UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr())) + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs new file mode 100644 index 00000000000..d1d180e4825 --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -0,0 +1,189 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use fortanix_sgx_abi::*; + +use io::{Error as IoError, Result as IoResult}; +use time::Duration; + +pub mod alloc; +#[macro_use] +mod raw; + +pub(crate) fn copy_user_buffer(buf: &alloc::UserRef<ByteBuffer>) -> Vec<u8> { + unsafe { + let buf = buf.to_enclave(); + alloc::User::from_raw_parts(buf.data as _, buf.len).to_enclave() + } +} + +pub fn read(fd: Fd, buf: &mut [u8]) -> IoResult<usize> { + unsafe { + let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.len()); + let len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?; + userbuf[..len].copy_to_enclave(&mut buf[..len]); + Ok(len) + } +} + +pub fn write(fd: Fd, buf: &[u8]) -> IoResult<usize> { + unsafe { + let userbuf = alloc::User::new_from_enclave(buf); + raw::write(fd, userbuf.as_ptr(), userbuf.len()).from_sgx_result() + } +} + +pub fn flush(fd: Fd) -> IoResult<()> { + unsafe { raw::flush(fd).from_sgx_result() } +} + +pub fn close(fd: Fd) { + unsafe { raw::close(fd) } +} + +fn string_from_bytebuffer(buf: &alloc::UserRef<ByteBuffer>, usercall: &str, arg: &str) -> String { + String::from_utf8(copy_user_buffer(buf)) + .unwrap_or_else(|_| panic!("Usercall {}: expected {} to be valid UTF-8", usercall, arg)) +} + +pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> { + unsafe { + let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); + let mut local = alloc::User::<ByteBuffer>::uninitialized(); + let fd = raw::bind_stream( + addr_user.as_ptr(), + addr_user.len(), + local.as_raw_mut_ptr() + ).from_sgx_result()?; + let local = string_from_bytebuffer(&local, "bind_stream", "local_addr"); + Ok((fd, local)) + } +} + +pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> { + unsafe { + let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); + let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done + // without forcing coercion? + let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap()); + let fd = raw::accept_stream( + fd, + local.as_raw_mut_ptr(), + peer.as_raw_mut_ptr() + ).from_sgx_result()?; + let local = string_from_bytebuffer(&local, "accept_stream", "local_addr"); + let peer = string_from_bytebuffer(&peer, "accept_stream", "peer_addr"); + Ok((fd, local, peer)) + } +} + +pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> { + unsafe { + let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); + let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); + let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done + // without forcing coercion? + let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap()); + let fd = raw::connect_stream( + addr_user.as_ptr(), + addr_user.len(), + local.as_raw_mut_ptr(), + peer.as_raw_mut_ptr() + ).from_sgx_result()?; + let local = string_from_bytebuffer(&local, "connect_stream", "local_addr"); + let peer = string_from_bytebuffer(&peer, "connect_stream", "peer_addr"); + Ok((fd, local, peer)) + } +} + +pub fn launch_thread() -> IoResult<()> { + unsafe { raw::launch_thread().from_sgx_result() } +} + +pub fn exit(panic: bool) -> ! { + unsafe { raw::exit(panic) } +} + +pub fn wait(event_mask: u64, timeout: u64) -> IoResult<u64> { + unsafe { raw::wait(event_mask, timeout).from_sgx_result() } +} + +pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> { + unsafe { raw::send(event_set, tcs).from_sgx_result() } +} + +pub fn insecure_time() -> Duration { + let t = unsafe { raw::insecure_time() }; + Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _) +} + +pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> { + unsafe { raw::alloc(size, alignment).from_sgx_result() } +} + +pub use self::raw::free; + +fn check_os_error(err: Result) -> i32 { + // FIXME: not sure how to make sure all variants of Error are covered + if err == Error::NotFound as _ || + err == Error::PermissionDenied as _ || + err == Error::ConnectionRefused as _ || + err == Error::ConnectionReset as _ || + err == Error::ConnectionAborted as _ || + err == Error::NotConnected as _ || + err == Error::AddrInUse as _ || + err == Error::AddrNotAvailable as _ || + err == Error::BrokenPipe as _ || + err == Error::AlreadyExists as _ || + err == Error::WouldBlock as _ || + err == Error::InvalidInput as _ || + err == Error::InvalidData as _ || + err == Error::TimedOut as _ || + err == Error::WriteZero as _ || + err == Error::Interrupted as _ || + err == Error::Other as _ || + err == Error::UnexpectedEof as _ || + ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err) + { + err + } else { + panic!("Usercall: returned invalid error value {}", err) + } +} + +trait FromSgxResult { + type Return; + + fn from_sgx_result(self) -> IoResult<Self::Return>; +} + +impl<T> FromSgxResult for (Result, T) { + type Return = T; + + fn from_sgx_result(self) -> IoResult<Self::Return> { + if self.0 == RESULT_SUCCESS { + Ok(self.1) + } else { + Err(IoError::from_raw_os_error(check_os_error(self.0))) + } + } +} + +impl FromSgxResult for Result { + type Return = (); + + fn from_sgx_result(self) -> IoResult<Self::Return> { + if self == RESULT_SUCCESS { + Ok(()) + } else { + Err(IoError::from_raw_os_error(check_os_error(self))) + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/raw.rs b/src/libstd/sys/sgx/abi/usercalls/raw.rs new file mode 100644 index 00000000000..a28d41c1b74 --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/raw.rs @@ -0,0 +1,231 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +use fortanix_sgx_abi::*; + +use ptr::NonNull; + +#[repr(C)] +struct UsercallReturn(u64, u64); + +extern "C" { + fn usercall(nr: u64, p1: u64, p2: u64, _ignore: u64, p3: u64, p4: u64) -> UsercallReturn; +} + +unsafe fn do_usercall(nr: u64, p1: u64, p2: u64, p3: u64, p4: u64) -> (u64, u64) { + if nr==0 { panic!("Invalid usercall number {}",nr) } + let UsercallReturn(a, b) = usercall(nr,p1,p2,0,p3,p4); + (a, b) +} + +type Register = u64; + +trait RegisterArgument { + fn from_register(Register) -> Self; + fn into_register(self) -> Register; +} + +trait ReturnValue { + fn from_registers(call: &'static str, regs: (Register, Register)) -> Self; +} + +macro_rules! define_usercalls { + // Using `$r:tt` because `$r:ty` doesn't match ! in `clobber_diverging` + ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*) => { + #[repr(C)] + #[allow(non_camel_case_types)] + enum Usercalls { + __enclave_usercalls_invalid, + $($f,)* + } + + $(enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) $(-> $r)*);)* + }; +} + +macro_rules! define_usercalls_asm { + ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)*; )*) => { + macro_rules! usercalls_asm { + () => { + concat!( + ".equ usercall_nr_LAST, 0\n", + $( + ".equ usercall_nr_", stringify!($f), ", usercall_nr_LAST+1\n", + ".equ usercall_nr_LAST, usercall_nr_", stringify!($f), "\n" + ),* + ) + } + } + }; +} + +macro_rules! define_ra { + (< $i:ident > $t:ty) => { + impl<$i> RegisterArgument for $t { + fn from_register(a: Register) -> Self { + a as _ + } + fn into_register(self) -> Register { + self as _ + } + } + }; + ($i:ty as $t:ty) => { + impl RegisterArgument for $t { + fn from_register(a: Register) -> Self { + a as $i as _ + } + fn into_register(self) -> Register { + self as $i as _ + } + } + }; + ($t:ty) => { + impl RegisterArgument for $t { + fn from_register(a: Register) -> Self { + a as _ + } + fn into_register(self) -> Register { + self as _ + } + } + }; +} + +define_ra!(Register); +define_ra!(i64); +define_ra!(u32); +define_ra!(u32 as i32); +define_ra!(u16); +define_ra!(u16 as i16); +define_ra!(u8); +define_ra!(u8 as i8); +define_ra!(usize); +define_ra!(usize as isize); +define_ra!(<T> *const T); +define_ra!(<T> *mut T); + +impl RegisterArgument for bool { + fn from_register(a: Register) -> bool { + if a != 0 { + true + } else { + false + } + } + fn into_register(self) -> Register { + self as _ + } +} + +impl<T: RegisterArgument> RegisterArgument for Option<NonNull<T>> { + fn from_register(a: Register) -> Option<NonNull<T>> { + NonNull::new(a as _) + } + fn into_register(self) -> Register { + self.map_or(0 as _, NonNull::as_ptr) as _ + } +} + +impl ReturnValue for ! { + fn from_registers(call: &'static str, _regs: (Register, Register)) -> Self { + panic!("Usercall {}: did not expect to be re-entered", call); + } +} + +impl ReturnValue for () { + fn from_registers(call: &'static str, regs: (Register, Register)) -> Self { + assert_eq!(regs.0, 0, "Usercall {}: expected {} return value to be 0", call, "1st"); + assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd"); + () + } +} + +impl<T: RegisterArgument> ReturnValue for T { + fn from_registers(call: &'static str, regs: (Register, Register)) -> Self { + assert_eq!(regs.1, 0, "Usercall {}: expected {} return value to be 0", call, "2nd"); + T::from_register(regs.0) + } +} + +impl<T: RegisterArgument, U: RegisterArgument> ReturnValue for (T, U) { + fn from_registers(_call: &'static str, regs: (Register, Register)) -> Self { + ( + T::from_register(regs.0), + U::from_register(regs.1) + ) + } +} + +macro_rules! enclave_usercalls_internal_define_usercalls { + (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, + $n3:ident: $t3:ty, $n4:ident: $t4:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + RegisterArgument::into_register($n3), + RegisterArgument::into_register($n4), + )) + } + ); + (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + RegisterArgument::into_register($n3), + 0 + )) + } + ); + (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + 0,0 + )) + } + ); + (def fn $f:ident($n1:ident: $t1:ty) -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f($n1: $t1) -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + RegisterArgument::into_register($n1), + 0,0,0 + )) + } + ); + (def fn $f:ident() -> $r:ty) => ( + #[inline(always)] + pub unsafe fn $f() -> $r { + ReturnValue::from_registers(stringify!($f), do_usercall( + Usercalls::$f as Register, + 0,0,0,0 + )) + } + ); + (def fn $f:ident($($n:ident: $t:ty),*)) => ( + enclave_usercalls_internal_define_usercalls!(def fn $f($($n: $t),*) -> ()); + ); +} + +invoke_with_usercalls!(define_usercalls); +invoke_with_usercalls!(define_usercalls_asm); diff --git a/src/libstd/sys/sgx/alloc.rs b/src/libstd/sys/sgx/alloc.rs new file mode 100644 index 00000000000..83c20ace89b --- /dev/null +++ b/src/libstd/sys/sgx/alloc.rs @@ -0,0 +1,42 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate dlmalloc; + +use alloc::{GlobalAlloc, Layout, System}; + +use super::waitqueue::SpinMutex; + +// Using a SpinMutex because we never want to exit the enclave waiting for the +// allocator. +static DLMALLOC: SpinMutex<dlmalloc::Dlmalloc> = SpinMutex::new(dlmalloc::DLMALLOC_INIT); + +#[stable(feature = "alloc_system_type", since = "1.28.0")] +unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + DLMALLOC.lock().malloc(layout.size(), layout.align()) + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + DLMALLOC.lock().calloc(layout.size(), layout.align()) + } + + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + DLMALLOC.lock().free(ptr, layout.size(), layout.align()) + } + + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) + } +} diff --git a/src/libstd/sys/sgx/args.rs b/src/libstd/sys/sgx/args.rs new file mode 100644 index 00000000000..8fb35d7ef98 --- /dev/null +++ b/src/libstd/sys/sgx/args.rs @@ -0,0 +1,75 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use super::abi::usercalls::{copy_user_buffer, alloc, ByteBuffer}; +use sync::atomic::{AtomicUsize, Ordering}; +use sys::os_str::Buf; +use sys_common::FromInner; +use slice; + +static ARGS: AtomicUsize = AtomicUsize::new(0); +type ArgsStore = Vec<OsString>; + +pub unsafe fn init(argc: isize, argv: *const *const u8) { + if argc != 0 { + let args = alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _); + let args = args.iter() + .map( |a| OsString::from_inner(Buf { inner: copy_user_buffer(a) }) ) + .collect::<ArgsStore>(); + ARGS.store(Box::into_raw(Box::new(args)) as _, Ordering::Relaxed); + } +} + +pub unsafe fn cleanup() { + let args = ARGS.swap(0, Ordering::Relaxed); + if args != 0 { + drop(Box::<ArgsStore>::from_raw(args as _)) + } +} + +pub fn args() -> Args { + let args = unsafe { (ARGS.load(Ordering::Relaxed) as *const ArgsStore).as_ref() }; + if let Some(args) = args { + Args(args.iter()) + } else { + Args([].iter()) + } +} + +pub struct Args(slice::Iter<'static, OsString>); + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + self.0.as_slice() + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option<OsString> { + self.0.next().cloned() + } + fn size_hint(&self) -> (usize, Option<usize>) { + self.0.size_hint() + } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { + self.0.len() + } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.0.next_back().cloned() + } +} diff --git a/src/libstd/sys/sgx/backtrace.rs b/src/libstd/sys/sgx/backtrace.rs new file mode 100644 index 00000000000..ca4a7c9561c --- /dev/null +++ b/src/libstd/sys/sgx/backtrace.rs @@ -0,0 +1,37 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::unsupported; +use sys_common::backtrace::Frame; + +pub struct BacktraceContext; + +pub fn unwind_backtrace(_frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + unsupported() +} + +pub fn resolve_symname<F>(_frame: Frame, + _callback: F, + _: &BacktraceContext) -> io::Result<()> + where F: FnOnce(Option<&str>) -> io::Result<()> +{ + unsupported() +} + +pub fn foreach_symbol_fileline<F>(_: Frame, + _: F, + _: &BacktraceContext) -> io::Result<bool> + where F: FnMut(&[u8], u32) -> io::Result<()> +{ + unsupported() +} diff --git a/src/libstd/sys/sgx/cmath.rs b/src/libstd/sys/sgx/cmath.rs new file mode 100644 index 00000000000..0c1300f61f8 --- /dev/null +++ b/src/libstd/sys/sgx/cmath.rs @@ -0,0 +1,41 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +// These symbols are all defined in `compiler-builtins` +extern { + pub fn acos(n: f64) -> f64; + pub fn acosf(n: f32) -> f32; + pub fn asin(n: f64) -> f64; + pub fn asinf(n: f32) -> f32; + pub fn atan(n: f64) -> f64; + pub fn atan2(a: f64, b: f64) -> f64; + pub fn atan2f(a: f32, b: f32) -> f32; + pub fn atanf(n: f32) -> f32; + pub fn cbrt(n: f64) -> f64; + pub fn cbrtf(n: f32) -> f32; + pub fn cosh(n: f64) -> f64; + pub fn coshf(n: f32) -> f32; + pub fn expm1(n: f64) -> f64; + pub fn expm1f(n: f32) -> f32; + pub fn fdim(a: f64, b: f64) -> f64; + pub fn fdimf(a: f32, b: f32) -> f32; + pub fn hypot(x: f64, y: f64) -> f64; + pub fn hypotf(x: f32, y: f32) -> f32; + pub fn log1p(n: f64) -> f64; + pub fn log1pf(n: f32) -> f32; + pub fn sinh(n: f64) -> f64; + pub fn sinhf(n: f32) -> f32; + pub fn tan(n: f64) -> f64; + pub fn tanf(n: f32) -> f32; + pub fn tanh(n: f64) -> f64; + pub fn tanhf(n: f32) -> f32; +} diff --git a/src/libstd/sys/sgx/condvar.rs b/src/libstd/sys/sgx/condvar.rs new file mode 100644 index 00000000000..940f50f25b8 --- /dev/null +++ b/src/libstd/sys/sgx/condvar.rs @@ -0,0 +1,51 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys::mutex::Mutex; +use time::Duration; + +use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex}; + +pub struct Condvar { + inner: SpinMutex<WaitVariable<()>>, +} + +impl Condvar { + pub const fn new() -> Condvar { + Condvar { inner: SpinMutex::new(WaitVariable::new(())) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn notify_one(&self) { + let _ = WaitQueue::notify_one(self.inner.lock()); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let _ = WaitQueue::notify_all(self.inner.lock()); + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + let guard = self.inner.lock(); + mutex.unlock(); + WaitQueue::wait(guard); + mutex.lock() + } + + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("timeout not supported in SGX"); + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/src/libstd/sys/sgx/env.rs b/src/libstd/sys/sgx/env.rs new file mode 100644 index 00000000000..146ce02754b --- /dev/null +++ b/src/libstd/sys/sgx/env.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = ""; + pub const OS: &'static str = ""; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".sgxs"; + pub const DLL_EXTENSION: &'static str = "sgxs"; + pub const EXE_SUFFIX: &'static str = ".sgxs"; + pub const EXE_EXTENSION: &'static str = "sgxs"; +} diff --git a/src/libstd/sys/sgx/fd.rs b/src/libstd/sys/sgx/fd.rs new file mode 100644 index 00000000000..31c4199c6cd --- /dev/null +++ b/src/libstd/sys/sgx/fd.rs @@ -0,0 +1,58 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::Fd; + +use io; +use mem; +use sys_common::AsInner; +use super::abi::usercalls; + +#[derive(Debug)] +pub struct FileDesc { + fd: Fd, +} + +impl FileDesc { + pub fn new(fd: Fd) -> FileDesc { + FileDesc { fd: fd } + } + + pub fn raw(&self) -> Fd { self.fd } + + /// Extracts the actual filedescriptor without closing it. + pub fn into_raw(self) -> Fd { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + usercalls::read(self.fd, buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + usercalls::write(self.fd, buf) + } + + pub fn flush(&self) -> io::Result<()> { + usercalls::flush(self.fd) + } +} + +impl AsInner<Fd> for FileDesc { + fn as_inner(&self) -> &Fd { &self.fd } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + usercalls::close(self.fd) + } +} diff --git a/src/libstd/sys/sgx/fs.rs b/src/libstd/sys/sgx/fs.rs new file mode 100644 index 00000000000..1dcea3e8eac --- /dev/null +++ b/src/libstd/sys/sgx/fs.rs @@ -0,0 +1,304 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use fmt; +use hash::{Hash, Hasher}; +use io::{self, SeekFrom}; +use path::{Path, PathBuf}; +use sys::time::SystemTime; +use sys::{unsupported, Void}; + +pub struct File(Void); + +pub struct FileAttr(Void); + +pub struct ReadDir(Void); + +pub struct DirEntry(Void); + +#[derive(Clone, Debug)] +pub struct OpenOptions { } + +pub struct FilePermissions(Void); + +pub struct FileType(Void); + +#[derive(Debug)] +pub struct DirBuilder { } + +impl FileAttr { + pub fn size(&self) -> u64 { + match self.0 {} + } + + pub fn perm(&self) -> FilePermissions { + match self.0 {} + } + + pub fn file_type(&self) -> FileType { + match self.0 {} + } + + pub fn modified(&self) -> io::Result<SystemTime> { + match self.0 {} + } + + pub fn accessed(&self) -> io::Result<SystemTime> { + match self.0 {} + } + + pub fn created(&self) -> io::Result<SystemTime> { + match self.0 {} + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + match self.0 {} + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + match self.0 {} + } + + pub fn set_readonly(&mut self, _readonly: bool) { + match self.0 {} + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + match self.0 {} + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + match self.0 {} + } +} + +impl Eq for FilePermissions { +} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + match self.0 {} + } + + pub fn is_file(&self) -> bool { + match self.0 {} + } + + pub fn is_symlink(&self) -> bool { + match self.0 {} + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + match self.0 {} + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + match self.0 {} + } +} + +impl Eq for FileType { +} + +impl Hash for FileType { + fn hash<H: Hasher>(&self, _h: &mut H) { + match self.0 {} + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl Iterator for ReadDir { + type Item = io::Result<DirEntry>; + + fn next(&mut self) -> Option<io::Result<DirEntry>> { + match self.0 {} + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + match self.0 {} + } + + pub fn file_name(&self) -> OsString { + match self.0 {} + } + + pub fn metadata(&self) -> io::Result<FileAttr> { + match self.0 {} + } + + pub fn file_type(&self) -> io::Result<FileType> { + match self.0 {} + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { } + } + + pub fn read(&mut self, _read: bool) { } + pub fn write(&mut self, _write: bool) { } + pub fn append(&mut self, _append: bool) { } + pub fn truncate(&mut self, _truncate: bool) { } + pub fn create(&mut self, _create: bool) { } + pub fn create_new(&mut self, _create_new: bool) { } +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> { + unsupported() + } + + pub fn file_attr(&self) -> io::Result<FileAttr> { + match self.0 {} + } + + pub fn fsync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn datasync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + match self.0 {} + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn flush(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result<File> { + match self.0 {} + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { } + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub fn readdir(_p: &Path) -> io::Result<ReadDir> { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result<PathBuf> { + unsupported() +} + +pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result<FileAttr> { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result<FileAttr> { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> { + unsupported() +} diff --git a/src/libstd/sys/sgx/memchr.rs b/src/libstd/sys/sgx/memchr.rs new file mode 100644 index 00000000000..0998bc5db4c --- /dev/null +++ b/src/libstd/sys/sgx/memchr.rs @@ -0,0 +1,11 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use core::slice::memchr::{memchr, memrchr}; diff --git a/src/libstd/sys/sgx/mod.rs b/src/libstd/sys/sgx/mod.rs new file mode 100644 index 00000000000..dd6862e908e --- /dev/null +++ b/src/libstd/sys/sgx/mod.rs @@ -0,0 +1,153 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! System bindings for the Fortanix SGX platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for Fortanix SGX. + +use io; +use os::raw::c_char; +use sync::atomic::{AtomicBool, Ordering}; + +pub mod abi; +mod waitqueue; + +pub mod alloc; +pub mod args; +#[cfg(feature = "backtrace")] +pub mod backtrace; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod fd; +pub mod fs; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod thread; +pub mod thread_local; +pub mod time; +pub mod stdio; + +#[cfg(not(test))] +pub fn init() { +} + +/// This function is used to implement functionality that simply doesn't exist. +/// Programs relying on this functionality will need to deal with the error. +pub fn unsupported<T>() -> io::Result<T> { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> io::Error { + io::Error::new(io::ErrorKind::Other, + "operation not supported on SGX yet") +} + +/// This function is used to implement various functions that doesn't exist, +/// but the lack of which might not be reason for error. If no error is +/// returned, the program might very well be able to function normally. This is +/// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is +/// `false`, the behavior is the same as `unsupported`. +pub fn sgx_ineffective<T>(v: T) -> io::Result<T> { + static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); + if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { + Err(io::Error::new(io::ErrorKind::Other, + "operation can't be trusted to have any effect on SGX")) + } else { + Ok(v) + } +} + +pub fn decode_error_kind(code: i32) -> io::ErrorKind { + use fortanix_sgx_abi::Error; + + // FIXME: not sure how to make sure all variants of Error are covered + if code == Error::NotFound as _ { + io::ErrorKind::NotFound + } else if code == Error::PermissionDenied as _ { + io::ErrorKind::PermissionDenied + } else if code == Error::ConnectionRefused as _ { + io::ErrorKind::ConnectionRefused + } else if code == Error::ConnectionReset as _ { + io::ErrorKind::ConnectionReset + } else if code == Error::ConnectionAborted as _ { + io::ErrorKind::ConnectionAborted + } else if code == Error::NotConnected as _ { + io::ErrorKind::NotConnected + } else if code == Error::AddrInUse as _ { + io::ErrorKind::AddrInUse + } else if code == Error::AddrNotAvailable as _ { + io::ErrorKind::AddrNotAvailable + } else if code == Error::BrokenPipe as _ { + io::ErrorKind::BrokenPipe + } else if code == Error::AlreadyExists as _ { + io::ErrorKind::AlreadyExists + } else if code == Error::WouldBlock as _ { + io::ErrorKind::WouldBlock + } else if code == Error::InvalidInput as _ { + io::ErrorKind::InvalidInput + } else if code == Error::InvalidData as _ { + io::ErrorKind::InvalidData + } else if code == Error::TimedOut as _ { + io::ErrorKind::TimedOut + } else if code == Error::WriteZero as _ { + io::ErrorKind::WriteZero + } else if code == Error::Interrupted as _ { + io::ErrorKind::Interrupted + } else if code == Error::Other as _ { + io::ErrorKind::Other + } else if code == Error::UnexpectedEof as _ { + io::ErrorKind::UnexpectedEof + } else { + io::ErrorKind::Other + } +} + +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + +pub unsafe fn strlen(mut s: *const c_char) -> usize { + let mut n = 0; + while *s != 0 { + n += 1; + s = s.offset(1); + } + return n +} + +pub unsafe fn abort_internal() -> ! { + abi::panic::panic_exit() +} + +pub fn hashmap_random_keys() -> (u64, u64) { + fn rdrand64() -> u64 { + unsafe { + let mut ret: u64 = ::mem::uninitialized(); + for _ in 0..10 { + if ::arch::x86_64::_rdrand64_step(&mut ret) == 1 { + return ret; + } + } + panic!("Failed to obtain random data"); + } + } + (rdrand64(), rdrand64()) +} diff --git a/src/libstd/sys/sgx/mutex.rs b/src/libstd/sys/sgx/mutex.rs new file mode 100644 index 00000000000..994cf91eea0 --- /dev/null +++ b/src/libstd/sys/sgx/mutex.rs @@ -0,0 +1,150 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::Tcs; + +use super::abi::thread; + +use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false}; + +pub struct Mutex { + inner: SpinMutex<WaitVariable<bool>>, +} + +// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 +impl Mutex { + pub const fn new() -> Mutex { + Mutex { inner: SpinMutex::new(WaitVariable::new(false)) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn lock(&self) { + let mut guard = self.inner.lock(); + if *guard.lock_var() { + // Another thread has the lock, wait + WaitQueue::wait(guard) + // Another thread has passed the lock to us + } else { + // We are just now obtaining the lock + *guard.lock_var_mut() = true; + } + } + + #[inline] + pub unsafe fn unlock(&self) { + let guard = self.inner.lock(); + if let Err(mut guard) = WaitQueue::notify_one(guard) { + // No other waiters, unlock + *guard.lock_var_mut() = false; + } else { + // There was a thread waiting, just pass the lock + } + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let mut guard = try_lock_or_false!(self.inner); + if *guard.lock_var() { + // Another thread has the lock + false + } else { + // We are just now obtaining the lock + *guard.lock_var_mut() = true; + true + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} + +struct ReentrantLock { + owner: Option<Tcs>, + count: usize +} + +pub struct ReentrantMutex { + inner: SpinMutex<WaitVariable<ReentrantLock>>, +} + +impl ReentrantMutex { + pub const fn uninitialized() -> ReentrantMutex { + ReentrantMutex { + inner: SpinMutex::new(WaitVariable::new(ReentrantLock { owner: None, count: 0 })) + } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn lock(&self) { + let mut guard = self.inner.lock(); + match guard.lock_var().owner { + Some(tcs) if tcs != thread::current() => { + // Another thread has the lock, wait + WaitQueue::wait(guard); + // Another thread has passed the lock to us + }, + _ => { + // We are just now obtaining the lock + guard.lock_var_mut().owner = Some(thread::current()); + guard.lock_var_mut().count += 1; + }, + } + } + + #[inline] + pub unsafe fn unlock(&self) { + let mut guard = self.inner.lock(); + if guard.lock_var().count > 1 { + guard.lock_var_mut().count -= 1; + } else { + match WaitQueue::notify_one(guard) { + Err(mut guard) => { + // No other waiters, unlock + guard.lock_var_mut().count = 0; + guard.lock_var_mut().owner = None; + }, + Ok(mut guard) => { + // There was a thread waiting, just pass the lock + if let NotifiedTcs::Single(tcs) = guard.notified_tcs() { + guard.lock_var_mut().owner = Some(tcs) + } else { + unreachable!() // called notify_one + } + } + } + } + } + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let mut guard = try_lock_or_false!(self.inner); + match guard.lock_var().owner { + Some(tcs) if tcs != thread::current() => { + // Another thread has the lock + false + }, + _ => { + // We are just now obtaining the lock + guard.lock_var_mut().owner = Some(thread::current()); + guard.lock_var_mut().count += 1; + true + }, + } + } + + #[inline] + pub unsafe fn destroy(&self) {} +} diff --git a/src/libstd/sys/sgx/net.rs b/src/libstd/sys/sgx/net.rs new file mode 100644 index 00000000000..176d230846d --- /dev/null +++ b/src/libstd/sys/sgx/net.rs @@ -0,0 +1,415 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt; +use io; +use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr, ToSocketAddrs}; +use time::Duration; +use sys::{unsupported, Void, sgx_ineffective}; +use sys::fd::FileDesc; +use convert::TryFrom; +use error; +use sync::Arc; + +use super::abi::usercalls; + +const DEFAULT_FAKE_TTL: u32 = 64; + +#[derive(Debug, Clone)] +struct Socket { + inner: Arc<FileDesc>, + local_addr: String, +} + +impl Socket { + fn new(fd: usercalls::Fd, local_addr: String) -> Socket { + Socket { inner: Arc::new(FileDesc::new(fd)), local_addr } + } +} + +#[derive(Debug, Clone)] +pub struct TcpStream { + inner: Socket, + peer_addr: String, +} + +fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> { + match result { + Ok(saddr) => Ok(saddr.to_string()), + // need to downcast twice because io::Error::into_inner doesn't return the original + // value if the conversion fails + Err(e) => if e.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()).is_some() { + Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host) + } else { + Err(e) + } + } +} + +fn addr_to_sockaddr(addr: &str) -> io::Result<SocketAddr> { + // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry + addr.to_socket_addrs().map(|mut it| it.next().unwrap()) +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; + Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr }) + } + + pub fn connect_timeout(addr: &SocketAddr, _: Duration) -> io::Result<TcpStream> { + Self::connect(Ok(addr)) // FIXME: ignoring timeout + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + sgx_ineffective(None) + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + sgx_ineffective(None) + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + Ok(0) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + self.inner.inner.read(buf) + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + self.inner.inner.write(buf) + } + + pub fn peer_addr(&self) -> io::Result<SocketAddr> { + addr_to_sockaddr(&self.peer_addr) + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + addr_to_sockaddr(&self.inner.local_addr) + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn duplicate(&self) -> io::Result<TcpStream> { + Ok(self.clone()) + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn nodelay(&self) -> io::Result<bool> { + sgx_ineffective(false) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result<u32> { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +#[derive(Debug, Clone)] +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr) = usercalls::bind_stream(&addr)?; + Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + addr_to_sockaddr(&self.inner.local_addr) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?; + let ret_peer = addr_to_sockaddr(&peer_addr).unwrap_or_else(|_| ([0; 4], 0).into()); + Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer)) + } + + pub fn duplicate(&self) -> io::Result<TcpListener> { + Ok(self.clone()) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result<u32> { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn only_v6(&self) -> io::Result<bool> { + sgx_ineffective(false) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +pub struct UdpSocket(Void); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result<SocketAddr> { + match self.0 {} + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result<UdpSocket> { + match self.0 {} + } + + pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result<Option<Duration>> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result<Option<Duration>> { + match self.0 {} + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn broadcast(&self) -> io::Result<bool> { + match self.0 {} + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v4(&self) -> io::Result<bool> { + match self.0 {} + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_ttl_v4(&self) -> io::Result<u32> { + match self.0 {} + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v6(&self) -> io::Result<bool> { + match self.0 {} + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result<u32> { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn send(&self, _: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +#[derive(Debug)] +pub struct NonIpSockAddr { + host: String +} + +impl error::Error for NonIpSockAddr { + fn description(&self) -> &str { + "Failed to convert address to SocketAddr" + } +} + +impl fmt::Display for NonIpSockAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Failed to convert address to SocketAddr: {}", self.host) + } +} + +pub struct LookupHost(Void); + +impl LookupHost { + fn new(host: String) -> io::Result<LookupHost> { + Err(io::Error::new(io::ErrorKind::Other, NonIpSockAddr { host })) + } + + pub fn port(&self) -> u16 { + match self.0 {} + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option<SocketAddr> { + match self.0 {} + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(v: &'a str) -> io::Result<LookupHost> { + LookupHost::new(v.to_owned()) + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> { + LookupHost::new(format!("{}:{}", host, port)) + } +} + +#[allow(bad_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr { + } + + pub type socklen_t = usize; +} diff --git a/src/libstd/sys/sgx/os.rs b/src/libstd/sys/sgx/os.rs new file mode 100644 index 00000000000..79ebafe73f9 --- /dev/null +++ b/src/libstd/sys/sgx/os.rs @@ -0,0 +1,147 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io; +use path::{self, PathBuf}; +use str; +use sys::{unsupported, Void, sgx_ineffective, decode_error_kind}; +use collections::HashMap; +use vec; +use sync::Mutex; +use sync::atomic::{AtomicUsize, Ordering}; +use sync::Once; + +pub fn errno() -> i32 { + RESULT_SUCCESS +} + +pub fn error_string(errno: i32) -> String { + if errno == RESULT_SUCCESS { + "operation succesful".into() + } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { + format!("user-specified error {:08x}", errno) + } else { + decode_error_kind(errno).as_str().into() + } +} + +pub fn getcwd() -> io::Result<PathBuf> { + unsupported() +} + +pub fn chdir(_: &path::Path) -> io::Result<()> { + sgx_ineffective(()) +} + +pub struct SplitPaths<'a>(&'a Void); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { + panic!("unsupported") +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option<PathBuf> { + match *self.0 {} + } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError> + where I: Iterator<Item=T>, T: AsRef<OsStr> +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "not supported in SGX yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { + "not supported in SGX yet" + } +} + +pub fn current_exe() -> io::Result<PathBuf> { + unsupported() +} + +static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV_INIT: Once = Once::new(); +type EnvStore = Mutex<HashMap<OsString, OsString>>; + +fn get_env_store() -> Option<&'static EnvStore> { + unsafe { (ENV.load(Ordering::Relaxed) as *const EnvStore).as_ref() } +} + +fn create_env_store() -> &'static EnvStore { + ENV_INIT.call_once(|| { + ENV.store(Box::into_raw(Box::new(EnvStore::default())) as _, Ordering::Relaxed) + }); + unsafe { + &*(ENV.load(Ordering::Relaxed) as *const EnvStore) + } +} + +pub type Env = vec::IntoIter<(OsString, OsString)>; + +pub fn env() -> Env { + let clone_to_vec = |map: &HashMap<OsString, OsString>| -> Vec<_> { + map.iter().map(|(k, v)| (k.clone(), v.clone()) ).collect() + }; + + get_env_store() + .map(|env| clone_to_vec(&env.lock().unwrap()) ) + .unwrap_or_default() + .into_iter() +} + +pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { + Ok(get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned() )) +} + +pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { + let (k, v) = (k.to_owned(), v.to_owned()); + create_env_store().lock().unwrap().insert(k, v); + Ok(()) +} + +pub fn unsetenv(k: &OsStr) -> io::Result<()> { + if let Some(env) = get_env_store() { + env.lock().unwrap().remove(k); + } + Ok(()) +} + +pub fn temp_dir() -> PathBuf { + panic!("no filesystem in SGX") +} + +pub fn home_dir() -> Option<PathBuf> { + None +} + +pub fn exit(code: i32) -> ! { + super::abi::exit_with_code(code as _) +} + +pub fn getpid() -> u32 { + panic!("no pids in SGX") +} diff --git a/src/libstd/sys/sgx/os_str.rs b/src/libstd/sys/sgx/os_str.rs new file mode 100644 index 00000000000..9bfb84db209 --- /dev/null +++ b/src/libstd/sys/sgx/os_str.rs @@ -0,0 +1,189 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec<u8>`/`[u8]`. + +use borrow::Cow; +use fmt; +use str; +use mem; +use rc::Rc; +use sync::Arc; +use sys_common::{AsInner, IntoInner}; +use sys_common::bytestring::debug_fmt_bytestring; +use core::str::lossy::Utf8Lossy; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Vec<u8> +} + +pub struct Slice { + pub inner: [u8] +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + debug_fmt_bytestring(&self.inner, formatter) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +impl IntoInner<Vec<u8>> for Buf { + fn into_inner(self) -> Vec<u8> { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Vec::with_capacity(capacity) + } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(&*self.inner) } + } + + pub fn into_string(self) -> Result<String, Buf> { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } + + #[inline] + pub fn into_box(self) -> Box<Slice> { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box<Slice>) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + self.as_slice().into_rc() + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow<str> { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } + + #[inline] + pub fn into_box(&self) -> Box<Slice> { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box<Slice> { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc<Slice> { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc<Slice> { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } +} diff --git a/src/libstd/sys/sgx/path.rs b/src/libstd/sys/sgx/path.rs new file mode 100644 index 00000000000..afe0c490426 --- /dev/null +++ b/src/libstd/sys/sgx/path.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use path::Prefix; +use ffi::OsStr; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { + None +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/sgx/pipe.rs b/src/libstd/sys/sgx/pipe.rs new file mode 100644 index 00000000000..6c6cbc14a8a --- /dev/null +++ b/src/libstd/sys/sgx/pipe.rs @@ -0,0 +1,35 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result<usize> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec<u8>, + _p2: AnonPipe, + _v2: &mut Vec<u8>) -> io::Result<()> { + match p1.0 {} +} diff --git a/src/libstd/sys/sgx/process.rs b/src/libstd/sys/sgx/process.rs new file mode 100644 index 00000000000..01a12fba043 --- /dev/null +++ b/src/libstd/sys/sgx/process.rs @@ -0,0 +1,162 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use fmt; +use io; +use sys::fs::File; +use sys::pipe::AnonPipe; +use sys::{unsupported, Void}; +use sys_common::process::{CommandEnv, DefaultEnvKey}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + env: CommandEnv<DefaultEnvKey> +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option<AnonPipe>, + pub stdout: Option<AnonPipe>, + pub stderr: Option<AnonPipe>, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command { + env: Default::default() + } + } + + pub fn arg(&mut self, _arg: &OsStr) { + } + + pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { + &mut self.env + } + + pub fn cwd(&mut self, _dir: &OsStr) { + } + + pub fn stdin(&mut self, _stdin: Stdio) { + } + + pub fn stdout(&mut self, _stdout: Stdio) { + } + + pub fn stderr(&mut self, _stderr: Stdio) { + } + + pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From<AnonPipe> for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From<File> for Stdio { + fn from(file: File) -> Stdio { + file.diverge() + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option<i32> { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus { +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(bool); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(false); + pub const FAILURE: ExitCode = ExitCode(true); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { + match self.0 {} + } +} diff --git a/src/libstd/sys/sgx/rwlock.rs b/src/libstd/sys/sgx/rwlock.rs new file mode 100644 index 00000000000..d1af98bd4f5 --- /dev/null +++ b/src/libstd/sys/sgx/rwlock.rs @@ -0,0 +1,258 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use num::NonZeroUsize; +use slice; +use str; + +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 + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + readers: SpinMutex::new(WaitVariable::new(None)), + writer: SpinMutex::new(WaitVariable::new(false)), + } + } + + #[inline] + pub unsafe fn read(&self) { + let mut rguard = self.readers.lock(); + let wguard = self.writer.lock(); + if *wguard.lock_var() || !wguard.queue_empty() { + // Another thread has or is waiting for the write lock, wait + drop(wguard); + WaitQueue::wait(rguard); + // Another thread has passed the lock to us + } else { + // No waiting writers, acquire the read lock + *rguard.lock_var_mut() = + NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); + } + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + let mut rguard = try_lock_or_false!(self.readers); + let wguard = try_lock_or_false!(self.writer); + if *wguard.lock_var() || !wguard.queue_empty() { + // Another thread has or is waiting for the write lock + false + } else { + // No waiting writers, acquire the read lock + *rguard.lock_var_mut() = + NonZeroUsize::new(rguard.lock_var().map_or(0, |n| n.get()) + 1); + true + } + } + + #[inline] + pub unsafe fn write(&self) { + let rguard = self.readers.lock(); + let mut wguard = self.writer.lock(); + if *wguard.lock_var() || rguard.lock_var().is_some() { + // Another thread has the lock, wait + drop(rguard); + WaitQueue::wait(wguard); + // Another thread has passed the lock to us + } else { + // We are just now obtaining the lock + *wguard.lock_var_mut() = true; + } + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + let rguard = try_lock_or_false!(self.readers); + let mut wguard = try_lock_or_false!(self.writer); + if *wguard.lock_var() || rguard.lock_var().is_some() { + // Another thread has the lock + false + } else { + // We are just now obtaining the lock + *wguard.lock_var_mut() = true; + true + } + } + + #[inline] + 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 + } else { + if let Ok(mut wguard) = WaitQueue::notify_one(wguard) { + // A writer was waiting, pass the lock + *wguard.lock_var_mut() = true; + } else { + // No writers were waiting, the lock is released + assert!(rguard.queue_empty()); + } + } + } + + #[inline] + 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; + if let Ok(mut rguard) = WaitQueue::notify_all(rguard) { + // One or more readers were waiting, pass the lock to them + if let NotifiedTcs::All { count } = rguard.notified_tcs() { + *rguard.lock_var_mut() = Some(count) + } else { + unreachable!() // called notify_all + } + } else { + // No readers waiting, the lock is released + } + } else { + // There was a thread waiting for write, just pass the lock + } + } + + #[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/libstd/sys/sgx/stack_overflow.rs b/src/libstd/sys/sgx/stack_overflow.rs new file mode 100644 index 00000000000..0176b748a87 --- /dev/null +++ b/src/libstd/sys/sgx/stack_overflow.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { +} + +pub unsafe fn cleanup() { +} diff --git a/src/libstd/sys/sgx/stdio.rs b/src/libstd/sys/sgx/stdio.rs new file mode 100644 index 00000000000..13c91195569 --- /dev/null +++ b/src/libstd/sys/sgx/stdio.rs @@ -0,0 +1,81 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fortanix_sgx_abi as abi; + +use io; +use sys::fd::FileDesc; + +pub struct Stdin(()); +pub struct Stdout(()); +pub struct Stderr(()); + +fn with_std_fd<F: FnOnce(&FileDesc) -> R, R>(fd: abi::Fd, f: F) -> R { + let fd = FileDesc::new(fd); + let ret = f(&fd); + fd.into_raw(); + ret +} + +impl Stdin { + pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) } + + pub fn read(&self, data: &mut [u8]) -> io::Result<usize> { + with_std_fd(abi::FD_STDIN, |fd| fd.read(data)) + } +} + +impl Stdout { + pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + with_std_fd(abi::FD_STDOUT, |fd| fd.write(data)) + } + + pub fn flush(&self) -> io::Result<()> { + with_std_fd(abi::FD_STDOUT, |fd| fd.flush()) + } +} + +impl Stderr { + pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) } + + pub fn write(&self, data: &[u8]) -> io::Result<usize> { + with_std_fd(abi::FD_STDERR, |fd| fd.write(data)) + } + + pub fn flush(&self) -> io::Result<()> { + with_std_fd(abi::FD_STDERR, |fd| fd.flush()) + } +} + +// FIXME: right now this raw stderr handle is used in a few places because +// std::io::stderr_raw isn't exposed, but once that's exposed this impl +// should go away +impl io::Write for Stderr { + fn write(&mut self, data: &[u8]) -> io::Result<usize> { + Stderr::write(self, data) + } + + fn flush(&mut self) -> io::Result<()> { + Stderr::flush(self) + } +} + +pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; + +pub fn is_ebadf(err: &io::Error) -> bool { + // FIXME: Rust normally maps Unix EBADF to `Other` + err.raw_os_error() == Some(abi::Error::BrokenPipe as _) +} + +pub fn panic_output() -> Option<impl io::Write> { + super::abi::panic::SgxPanicOutput::new() +} diff --git a/src/libstd/sys/sgx/thread.rs b/src/libstd/sys/sgx/thread.rs new file mode 100644 index 00000000000..9f3c4536cb5 --- /dev/null +++ b/src/libstd/sys/sgx/thread.rs @@ -0,0 +1,100 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use boxed::FnBox; +use ffi::CStr; +use io; +use time::Duration; + +use super::abi::usercalls; + +pub struct Thread(task_queue::JoinHandle); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +mod task_queue { + use sync::{Mutex, MutexGuard, Once}; + use sync::mpsc; + use boxed::FnBox; + + pub type JoinHandle = mpsc::Receiver<()>; + + pub(super) struct Task { + p: Box<dyn FnBox()>, + done: mpsc::Sender<()>, + } + + impl Task { + pub(super) fn new(p: Box<dyn FnBox()>) -> (Task, JoinHandle) { + let (done, recv) = mpsc::channel(); + (Task { p, done }, recv) + } + + pub(super) fn run(self) { + (self.p)(); + let _ = self.done.send(()); + } + } + + static TASK_QUEUE_INIT: Once = Once::new(); + static mut TASK_QUEUE: Option<Mutex<Vec<Task>>> = None; + + pub(super) fn lock() -> MutexGuard<'static, Vec<Task>> { + unsafe { + TASK_QUEUE_INIT.call_once(|| TASK_QUEUE = Some(Default::default()) ); + TASK_QUEUE.as_ref().unwrap().lock().unwrap() + } + } +} + +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, p: Box<dyn FnBox()>) + -> io::Result<Thread> + { + let mut queue_lock = task_queue::lock(); + usercalls::launch_thread()?; + let (task, handle) = task_queue::Task::new(p); + queue_lock.push(task); + Ok(Thread(handle)) + } + + pub(super) fn entry() { + let mut guard = task_queue::lock(); + let task = guard.pop().expect("Thread started but no tasks pending"); + drop(guard); // make sure to not hold the task queue lock longer than necessary + task.run() + } + + pub fn yield_now() { + assert_eq!( + usercalls::wait(0, usercalls::WAIT_NO).unwrap_err().kind(), + io::ErrorKind::WouldBlock + ); + } + + pub fn set_name(_name: &CStr) { + // FIXME: could store this pointer in TLS somewhere + } + + pub fn sleep(_dur: Duration) { + panic!("can't sleep"); // FIXME + } + + pub fn join(self) { + let _ = self.0.recv(); + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option<Guard> { None } + pub unsafe fn init() -> Option<Guard> { None } +} diff --git a/src/libstd/sys/sgx/thread_local.rs b/src/libstd/sys/sgx/thread_local.rs new file mode 100644 index 00000000000..3b628bae4fb --- /dev/null +++ b/src/libstd/sys/sgx/thread_local.rs @@ -0,0 +1,38 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::abi::tls::{Tls, Key as AbiKey}; + +pub type Key = usize; + +#[inline] +pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + Tls::create(dtor).as_usize() +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + Tls::set(AbiKey::from_usize(key), value) +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + Tls::get(AbiKey::from_usize(key)) +} + +#[inline] +pub unsafe fn destroy(key: Key) { + Tls::destroy(AbiKey::from_usize(key)) +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/sgx/time.rs b/src/libstd/sys/sgx/time.rs new file mode 100644 index 00000000000..196e1a97fc4 --- /dev/null +++ b/src/libstd/sys/sgx/time.rs @@ -0,0 +1,57 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use time::Duration; +use super::abi::usercalls; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + Instant(usercalls::insecure_time()) + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + self.0 - other.0 + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + SystemTime(usercalls::insecure_time()) + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result<Duration, Duration> { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_add(*other)?)) + } + + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) + } +} diff --git a/src/libstd/sys/sgx/waitqueue.rs b/src/libstd/sys/sgx/waitqueue.rs new file mode 100644 index 00000000000..ef0def13eee --- /dev/null +++ b/src/libstd/sys/sgx/waitqueue.rs @@ -0,0 +1,549 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// A simple queue implementation for synchronization primitives. +/// +/// This queue is used to implement condition variable and mutexes. +/// +/// Users of this API are expected to use the `WaitVariable<T>` type. Since +/// that type is not `Sync`, it needs to be protected by e.g. a `SpinMutex` to +/// allow shared access. +/// +/// Since userspace may send spurious wake-ups, the wakeup event state is +/// recorded in the enclave. The wakeup event state is protected by a spinlock. +/// The queue and associated wait state are stored in a `WaitVariable`. + +use ops::{Deref, DerefMut}; +use num::NonZeroUsize; + +use fortanix_sgx_abi::{Tcs, EV_UNPARK, WAIT_INDEFINITE}; +use super::abi::usercalls; +use super::abi::thread; + +use self::unsafe_list::{UnsafeList, UnsafeListEntry}; +pub use self::spin_mutex::{SpinMutex, SpinMutexGuard, try_lock_or_false}; + +/// An queue entry in a `WaitQueue`. +struct WaitEntry { + /// TCS address of the thread that is waiting + tcs: Tcs, + /// Whether this thread has been notified to be awoken + wake: bool +} + +/// Data stored with a `WaitQueue` alongside it. This ensures accesses to the +/// queue and the data are synchronized, since the type itself is not `Sync`. +/// +/// Consumers of this API should use a synchronization primitive for shared +/// access, such as `SpinMutex`. +#[derive(Default)] +pub struct WaitVariable<T> { + queue: WaitQueue, + lock: T +} + +impl<T> WaitVariable<T> { + pub const fn new(var: T) -> Self { + WaitVariable { + queue: WaitQueue::new(), + lock: var + } + } + + pub fn queue_empty(&self) -> bool { + self.queue.is_empty() + } + + pub fn lock_var(&self) -> &T { + &self.lock + } + + pub fn lock_var_mut(&mut self) -> &mut T { + &mut self.lock + } +} + +#[derive(Copy, Clone)] +pub enum NotifiedTcs { + Single(Tcs), + All { count: NonZeroUsize } +} + +/// An RAII guard that will notify a set of target threads as well as unlock +/// a mutex on drop. +pub struct WaitGuard<'a, T: 'a> { + mutex_guard: Option<SpinMutexGuard<'a, WaitVariable<T>>>, + notified_tcs: NotifiedTcs +} + +/// A queue of threads that are waiting on some synchronization primitive. +/// +/// `UnsafeList` entries are allocated on the waiting thread's stack. This +/// avoids any global locking that might happen in the heap allocator. This is +/// safe because the waiting thread will not return from that stack frame until +/// after it is notified. The notifying thread ensures to clean up any +/// references to the list entries before sending the wakeup event. +pub struct WaitQueue { + // We use an inner Mutex here to protect the data in the face of spurious + // wakeups. + inner: UnsafeList<SpinMutex<WaitEntry>>, +} +unsafe impl Send for WaitQueue {} + +impl Default for WaitQueue { + fn default() -> Self { + Self::new() + } +} + +impl<'a, T> WaitGuard<'a, T> { + /// Returns which TCSes will be notified when this guard drops. + pub fn notified_tcs(&self) -> NotifiedTcs { + self.notified_tcs + } +} + +impl<'a, T> Deref for WaitGuard<'a, T> { + type Target = SpinMutexGuard<'a, WaitVariable<T>>; + + fn deref(&self) -> &Self::Target { + self.mutex_guard.as_ref().unwrap() + } +} + +impl<'a, T> DerefMut for WaitGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.mutex_guard.as_mut().unwrap() + } +} + +impl<'a, T> Drop for WaitGuard<'a, T> { + fn drop(&mut self) { + drop(self.mutex_guard.take()); + let target_tcs = match self.notified_tcs { + NotifiedTcs::Single(tcs) => Some(tcs), + NotifiedTcs::All { .. } => None + }; + usercalls::send(EV_UNPARK, target_tcs).unwrap(); + } +} + +impl WaitQueue { + pub const fn new() -> Self { + WaitQueue { + inner: UnsafeList::new() + } + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Add the calling thread to the WaitVariable's wait queue, then wait + /// until a wakeup event. + /// + /// This function does not return until this thread has been awoken. + pub fn wait<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) { + unsafe { + let mut entry = UnsafeListEntry::new(SpinMutex::new(WaitEntry { + tcs: thread::current(), + wake: false + })); + let entry = guard.queue.inner.push(&mut entry); + drop(guard); + while !entry.lock().wake { + assert_eq!( + usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap() & EV_UNPARK, + EV_UNPARK + ); + } + } + } + + /// Either find the next waiter on the wait queue, or return the mutex + /// guard unchanged. + /// + /// If a waiter is found, a `WaitGuard` is returned which will notify the + /// waiter when it is dropped. + pub fn notify_one<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) + -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>> + { + unsafe { + if let Some(entry) = guard.queue.inner.pop() { + let mut entry_guard = entry.lock(); + let tcs = entry_guard.tcs; + entry_guard.wake = true; + drop(entry); + Ok(WaitGuard { + mutex_guard: Some(guard), + notified_tcs: NotifiedTcs::Single(tcs) + }) + } else { + Err(guard) + } + } + } + + /// Either find any and all waiters on the wait queue, or return the mutex + /// guard unchanged. + /// + /// If at least one waiter is found, a `WaitGuard` is returned which will + /// notify all waiters when it is dropped. + pub fn notify_all<T>(mut guard: SpinMutexGuard<WaitVariable<T>>) + -> Result<WaitGuard<T>, SpinMutexGuard<WaitVariable<T>>> + { + unsafe { + let mut count = 0; + while let Some(entry) = guard.queue.inner.pop() { + count += 1; + let mut entry_guard = entry.lock(); + entry_guard.wake = true; + } + if let Some(count) = NonZeroUsize::new(count) { + Ok(WaitGuard { + mutex_guard: Some(guard), + notified_tcs: NotifiedTcs::All { count } + }) + } else { + Err(guard) + } + } + } +} + +/// A doubly-linked list where callers are in charge of memory allocation +/// of the nodes in the list. +mod unsafe_list { + use ptr::NonNull; + use mem; + + pub struct UnsafeListEntry<T> { + next: NonNull<UnsafeListEntry<T>>, + prev: NonNull<UnsafeListEntry<T>>, + value: Option<T> + } + + impl<T> UnsafeListEntry<T> { + fn dummy() -> Self { + UnsafeListEntry { + next: NonNull::dangling(), + prev: NonNull::dangling(), + value: None + } + } + + pub fn new(value: T) -> Self { + UnsafeListEntry { + value: Some(value), + ..Self::dummy() + } + } + } + + pub struct UnsafeList<T> { + head_tail: NonNull<UnsafeListEntry<T>>, + head_tail_entry: Option<UnsafeListEntry<T>>, + } + + impl<T> UnsafeList<T> { + pub const fn new() -> Self { + unsafe { + UnsafeList { + head_tail: NonNull::new_unchecked(1 as _), + head_tail_entry: None + } + } + } + + unsafe fn init(&mut self) { + if self.head_tail_entry.is_none() { + self.head_tail_entry = Some(UnsafeListEntry::dummy()); + self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()); + self.head_tail.as_mut().next = self.head_tail; + self.head_tail.as_mut().prev = self.head_tail; + } + } + + pub fn is_empty(&self) -> bool { + unsafe { + if self.head_tail_entry.is_some() { + let first = self.head_tail.as_ref().next; + if first == self.head_tail { + // ,-------> /---------\ next ---, + // | |head_tail| | + // `--- prev \---------/ <-------` + assert_eq!(self.head_tail.as_ref().prev, first); + true + } else { + false + } + } else { + true + } + } + } + + /// Pushes an entry onto the back of the list. + /// + /// # Safety + /// + /// The entry must remain allocated until the entry is removed from the + /// list AND the caller who popped is done using the entry. + pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry<T>) -> &'a T { + self.init(); + + // BEFORE: + // /---------\ next ---> /---------\ + // ... |prev_tail| |head_tail| ... + // \---------/ <--- prev \---------/ + // + // AFTER: + // /---------\ next ---> /-----\ next ---> /---------\ + // ... |prev_tail| |entry| |head_tail| ... + // \---------/ <--- prev \-----/ <--- prev \---------/ + let mut entry = NonNull::new_unchecked(entry); + let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry); + entry.as_mut().prev = prev_tail; + entry.as_mut().next = self.head_tail; + prev_tail.as_mut().next = entry; + (*entry.as_ptr()).value.as_ref().unwrap() + } + + /// Pops an entry from the front of the list. + /// + /// # Safety + /// + /// The caller must make sure to synchronize ending the borrow of the + /// return value and deallocation of the containing entry. + pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> { + self.init(); + + if self.is_empty() { + None + } else { + // BEFORE: + // /---------\ next ---> /-----\ next ---> /------\ + // ... |head_tail| |first| |second| ... + // \---------/ <--- prev \-----/ <--- prev \------/ + // + // AFTER: + // /---------\ next ---> /------\ + // ... |head_tail| |second| ... + // \---------/ <--- prev \------/ + let mut first = self.head_tail.as_mut().next; + let mut second = first.as_mut().next; + self.head_tail.as_mut().next = second; + second.as_mut().prev = self.head_tail; + first.as_mut().next = NonNull::dangling(); + first.as_mut().prev = NonNull::dangling(); + Some((*first.as_ptr()).value.as_ref().unwrap()) + } + } + } + + #[cfg(test)] + mod tests { + use super::*; + use cell::Cell; + + unsafe fn assert_empty<T>(list: &mut UnsafeList<T>) { + assert!(list.pop().is_none(), "assertion failed: list is not empty"); + } + + #[test] + fn init_empty() { + unsafe { + assert_empty(&mut UnsafeList::<i32>::new()); + } + } + + #[test] + fn push_pop() { + unsafe { + let mut node = UnsafeListEntry::new(1234); + let mut list = UnsafeList::new(); + assert_eq!(list.push(&mut node), &1234); + assert_eq!(list.pop().unwrap(), &1234); + assert_empty(&mut list); + } + } + + #[test] + fn complex_pushes_pops() { + unsafe { + let mut node1 = UnsafeListEntry::new(1234); + let mut node2 = UnsafeListEntry::new(4567); + let mut node3 = UnsafeListEntry::new(9999); + let mut node4 = UnsafeListEntry::new(8642); + let mut list = UnsafeList::new(); + list.push(&mut node1); + list.push(&mut node2); + assert_eq!(list.pop().unwrap(), &1234); + list.push(&mut node3); + assert_eq!(list.pop().unwrap(), &4567); + assert_eq!(list.pop().unwrap(), &9999); + assert_empty(&mut list); + list.push(&mut node4); + assert_eq!(list.pop().unwrap(), &8642); + assert_empty(&mut list); + } + } + + #[test] + fn cell() { + unsafe { + let mut node = UnsafeListEntry::new(Cell::new(0)); + let mut list = UnsafeList::new(); + let noderef = list.push(&mut node); + assert_eq!(noderef.get(), 0); + list.pop().unwrap().set(1); + assert_empty(&mut list); + assert_eq!(noderef.get(), 1); + } + } + } +} + +/// Trivial spinlock-based implementation of `sync::Mutex`. +// FIXME: Perhaps use Intel TSX to avoid locking? +mod spin_mutex { + use cell::UnsafeCell; + use sync::atomic::{AtomicBool, Ordering, spin_loop_hint}; + use ops::{Deref, DerefMut}; + + #[derive(Default)] + pub struct SpinMutex<T> { + value: UnsafeCell<T>, + lock: AtomicBool, + } + + unsafe impl<T: Send> Send for SpinMutex<T> {} + unsafe impl<T: Send> Sync for SpinMutex<T> {} + + pub struct SpinMutexGuard<'a, T: 'a> { + mutex: &'a SpinMutex<T>, + } + + impl<'a, T> !Send for SpinMutexGuard<'a, T> {} + unsafe impl<'a, T: Sync> Sync for SpinMutexGuard<'a, T> {} + + impl<T> SpinMutex<T> { + pub const fn new(value: T) -> Self { + SpinMutex { + value: UnsafeCell::new(value), + lock: AtomicBool::new(false) + } + } + + #[inline(always)] + pub fn lock(&self) -> SpinMutexGuard<T> { + loop { + match self.try_lock() { + None => while self.lock.load(Ordering::Relaxed) { + spin_loop_hint() + }, + Some(guard) => return guard + } + } + } + + #[inline(always)] + pub fn try_lock(&self) -> Option<SpinMutexGuard<T>> { + if !self.lock.compare_and_swap(false, true, Ordering::Acquire) { + Some(SpinMutexGuard { + mutex: self, + }) + } else { + None + } + } + } + + pub macro try_lock_or_false { + ($e:expr) => { + if let Some(v) = $e.try_lock() { + v + } else { + return false + } + } + } + + impl<'a, T> Deref for SpinMutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { + &*self.mutex.value.get() + } + } + } + + impl<'a, T> DerefMut for SpinMutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { + &mut*self.mutex.value.get() + } + } + } + + impl<'a, T> Drop for SpinMutexGuard<'a, T> { + fn drop(&mut self) { + self.mutex.lock.store(false, Ordering::Release) + } + } + + #[cfg(test)] + mod tests { + #![allow(deprecated)] + + use super::*; + use sync::Arc; + use thread; + + #[test] + fn sleep() { + let mutex = Arc::new(SpinMutex::<i32>::default()); + let mutex2 = mutex.clone(); + let guard = mutex.lock(); + let t1 = thread::spawn(move || { + *mutex2.lock() = 1; + }); + thread::sleep_ms(50); + assert_eq!(*guard, 0); + drop(guard); + t1.join().unwrap(); + assert_eq!(*mutex.lock(), 1); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sync::Arc; + use thread; + + #[test] + fn queue() { + let wq = Arc::new(SpinMutex::<WaitVariable<()>>::default()); + let wq2 = wq.clone(); + + let locked = wq.lock(); + + let t1 = thread::spawn(move || { + assert!(WaitQueue::notify_one(wq2.lock()).is_none()) + }); + + WaitQueue::wait(locked); + + t1.join().unwrap(); + } +} diff --git a/src/libstd/sys/unix/android.rs b/src/libstd/sys/unix/android.rs index 10436723a81..462eab56664 100644 --- a/src/libstd/sys/unix/android.rs +++ b/src/libstd/sys/unix/android.rs @@ -15,7 +15,7 @@ //! always work with the most recent version of Android, but we also want to //! work with older versions of Android for whenever projects need to. //! -//! Our current minimum supported Android version is `android-9`, e.g. Android +//! Our current minimum supported Android version is `android-9`, e.g., Android //! with API level 9. We then in theory want to work on that and all future //! versions of Android! //! diff --git a/src/libstd/sys/unix/env.rs b/src/libstd/sys/unix/env.rs index ad116c57f55..1b6838f0295 100644 --- a/src/libstd/sys/unix/env.rs +++ b/src/libstd/sys/unix/env.rs @@ -10,176 +10,176 @@ #[cfg(target_os = "linux")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "linux"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "linux"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "macos")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "macos"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".dylib"; - pub const DLL_EXTENSION: &'static str = "dylib"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "macos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "ios")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "ios"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".dylib"; - pub const DLL_EXTENSION: &'static str = "dylib"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "ios"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "freebsd")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "freebsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "freebsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "dragonfly")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "dragonfly"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "dragonfly"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "bitrig")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "bitrig"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "bitrig"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "netbsd")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "netbsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "netbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "openbsd")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "openbsd"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "openbsd"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "android")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "android"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "android"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "solaris")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "solaris"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "solaris"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "haiku")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "haiku"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "haiku"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(all(target_os = "emscripten", target_arch = "asmjs"))] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "emscripten"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".js"; - pub const EXE_EXTENSION: &'static str = "js"; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; } #[cfg(all(target_os = "emscripten", target_arch = "wasm32"))] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "emscripten"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ".js"; - pub const EXE_EXTENSION: &'static str = "js"; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "emscripten"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ".js"; + pub const EXE_EXTENSION: &str = "js"; } #[cfg(target_os = "fuchsia")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "fuchsia"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "fuchsia"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "l4re")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "l4re"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "l4re"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } #[cfg(target_os = "hermit")] pub mod os { - pub const FAMILY: &'static str = "unix"; - pub const OS: &'static str = "hermit"; - pub const DLL_PREFIX: &'static str = "lib"; - pub const DLL_SUFFIX: &'static str = ".so"; - pub const DLL_EXTENSION: &'static str = "so"; - pub const EXE_SUFFIX: &'static str = ""; - pub const EXE_EXTENSION: &'static str = ""; + pub const FAMILY: &str = "unix"; + pub const OS: &str = "hermit"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; } diff --git a/src/libstd/sys/unix/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index 7e65bbdef2a..af2f1998415 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -348,7 +348,7 @@ pub trait OpenOptionsExt { /// # Examples /// /// ```no_run - /// # #![feature(libc)] + /// # #![feature(rustc_private)] /// extern crate libc; /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index 737437c76b7..bcf0d440eba 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -132,7 +132,7 @@ impl SocketAddr { if len == 0 { // When there is a datagram from unnamed unix socket // linux returns zero bytes of address - len = sun_path_offset() as libc::socklen_t; // i.e. zero-length address + len = sun_path_offset() as libc::socklen_t; // i.e., zero-length address } else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t { return Err(io::Error::new(io::ErrorKind::InvalidInput, "file descriptor did not correspond to a Unix socket")); diff --git a/src/libstd/sys/unix/l4re.rs b/src/libstd/sys/unix/l4re.rs index 21218489679..bbb0fd45ba3 100644 --- a/src/libstd/sys/unix/l4re.rs +++ b/src/libstd/sys/unix/l4re.rs @@ -21,7 +21,7 @@ pub mod net { use sys_common::{AsInner, FromInner, IntoInner}; use sys::fd::FileDesc; use time::Duration; - + use convert::TryFrom; pub extern crate libc as netc; @@ -118,7 +118,7 @@ pub mod net { } impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { unimpl!(); } @@ -216,7 +216,7 @@ pub mod net { } impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { unimpl!(); } @@ -278,7 +278,7 @@ pub mod net { } impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { unimpl!(); } @@ -402,7 +402,7 @@ pub mod net { unimpl!(); } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { unimpl!(); } } @@ -431,11 +431,30 @@ pub mod net { } } + impl LookupHost { + pub fn port(&self) -> u16 { + unimpl!(); + } + } + unsafe impl Sync for LookupHost {} unsafe impl Send for LookupHost {} - pub fn lookup_host(_: &str) -> io::Result<LookupHost> { - unimpl!(); + + impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unimpl!(); + } + } + + impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unimpl!(); + } } } diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 2d10541752c..f30817e69ab 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -26,7 +26,7 @@ pub extern crate libc as netc; pub type wrlen_t = size_t; // See below for the usage of SOCK_CLOEXEC, but this constant is only defined on -// Linux currently (e.g. support doesn't exist on other platforms). In order to +// Linux currently (e.g., support doesn't exist on other platforms). In order to // get name resolution to work and things to compile we just define a dummy // SOCK_CLOEXEC here for other platforms. Note that the dummy constant isn't // actually ever used (the blocks below are wrapped in `if cfg!` as well. @@ -203,18 +203,21 @@ impl Socket { // Linux. This was added in 2.6.28, however, and because we support // 2.6.18 we must detect this support dynamically. if cfg!(target_os = "linux") { - weak! { - fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int + syscall! { + fn accept4( + fd: c_int, + addr: *mut sockaddr, + addr_len: *mut socklen_t, + flags: c_int + ) -> c_int } - if let Some(accept) = accept4.get() { - let res = cvt_r(|| unsafe { - accept(self.0.raw(), storage, len, SOCK_CLOEXEC) - }); - match res { - Ok(fd) => return Ok(Socket(FileDesc::new(fd))), - Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} - Err(e) => return Err(e), - } + let res = cvt_r(|| unsafe { + accept4(self.0.raw(), storage, len, SOCK_CLOEXEC) + }); + match res { + Ok(fd) => return Ok(Socket(FileDesc::new(fd))), + Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} + Err(e) => return Err(e), } } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index b387a8d59a5..6e8ee445994 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -67,7 +67,8 @@ pub fn errno() -> i32 { } /// Sets the platform-specific value of errno -#[cfg(any(target_os = "solaris", target_os = "fuchsia"))] // only needed for readdir so far +#[cfg(all(not(target_os = "linux"), + not(target_os = "dragonfly")))] // needed for readdir and syscall! pub fn set_errno(e: i32) { unsafe { *errno_location() = e as c_int @@ -84,6 +85,18 @@ pub fn errno() -> i32 { unsafe { errno as i32 } } +#[cfg(target_os = "dragonfly")] +pub fn set_errno(e: i32) { + extern { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { extern { @@ -283,11 +296,14 @@ pub fn current_exe() -> io::Result<PathBuf> { #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))] pub fn current_exe() -> io::Result<PathBuf> { - let selfexe = PathBuf::from("/proc/self/exe"); - if selfexe.exists() { - ::fs::read_link(selfexe) - } else { - Err(io::Error::new(io::ErrorKind::Other, "no /proc/self/exe available. Is /proc mounted?")) + match ::fs::read_link("/proc/self/exe") { + Err(ref e) if e.kind() == io::ErrorKind::NotFound => { + Err(io::Error::new( + io::ErrorKind::Other, + "no /proc/self/exe available. Is /proc mounted?" + )) + }, + other => other, } } diff --git a/src/libstd/sys/unix/path.rs b/src/libstd/sys/unix/path.rs index bf9af7a4353..834b4b448dc 100644 --- a/src/libstd/sys/unix/path.rs +++ b/src/libstd/sys/unix/path.rs @@ -25,5 +25,5 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { None } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 0a5dccdddda..24b2959a3fa 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -22,7 +22,7 @@ use sys::{cvt, cvt_r}; pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - weak! { fn pipe2(*mut c_int, c_int) -> c_int } + syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int } static INVALID: AtomicBool = ATOMIC_BOOL_INIT; let mut fds = [0; 2]; @@ -39,22 +39,20 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { !INVALID.load(Ordering::SeqCst) { - if let Some(pipe) = pipe2.get() { - // Note that despite calling a glibc function here we may still - // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to - // emulate on older kernels, so if you happen to be running on - // an older kernel you may see `pipe2` as a symbol but still not - // see the syscall. - match cvt(unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { - Ok(_) => { - return Ok((AnonPipe(FileDesc::new(fds[0])), - AnonPipe(FileDesc::new(fds[1])))); - } - Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => { - INVALID.store(true, Ordering::SeqCst); - } - Err(e) => return Err(e), + // Note that despite calling a glibc function here we may still + // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to + // emulate on older kernels, so if you happen to be running on + // an older kernel you may see `pipe2` as a symbol but still not + // see the syscall. + match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { + Ok(_) => { + return Ok((AnonPipe(FileDesc::new(fds[0])), + AnonPipe(FileDesc::new(fds[1])))); + } + Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => { + INVALID.store(true, Ordering::SeqCst); } + Err(e) => return Err(e), } } cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs index bfbf12f34ee..3248f424460 100644 --- a/src/libstd/sys/unix/process/process_unix.rs +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -22,7 +22,7 @@ use sys; impl Command { pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + const CLOEXEC_MSG_FOOTER: &[u8] = b"NOEX"; let envp = self.capture_env(); diff --git a/src/libstd/sys/unix/process/zircon.rs b/src/libstd/sys/unix/process/zircon.rs index a06c73ee263..0335c1e914c 100644 --- a/src/libstd/sys/unix/process/zircon.rs +++ b/src/libstd/sys/unix/process/zircon.rs @@ -215,7 +215,7 @@ pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002; // and has a closed remote end will return ERR_REMOTE_CLOSED. #[allow(unused)] pub const ERR_SHOULD_WAIT: zx_status_t = -22; -// ERR_CANCELED: The in-progress operation (e.g. a wait) has been +// ERR_CANCELED: The in-progress operation (e.g., a wait) has been // // canceled. #[allow(unused)] pub const ERR_CANCELED: zx_status_t = -23; diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs index 87ba2aef4f1..63e341abb2c 100644 --- a/src/libstd/sys/unix/stdio.rs +++ b/src/libstd/sys/unix/stdio.rs @@ -76,6 +76,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { pub const STDIN_BUF_SIZE: usize = ::sys_common::io::DEFAULT_BUF_SIZE; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index f3a45d24657..e0d2c620498 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -49,7 +49,8 @@ unsafe fn pthread_attr_setstacksize(_attr: *mut libc::pthread_attr_t, } impl Thread { - pub unsafe fn new<'a>(stack: usize, p: Box<dyn FnBox() + 'a>) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; let mut native: libc::pthread_t = mem::zeroed(); @@ -210,7 +211,6 @@ pub mod guard { pub type Guard = Range<usize>; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } @@ -354,26 +354,6 @@ pub mod guard { } } - pub unsafe fn deinit() { - if !cfg!(target_os = "linux") { - if let Some(stackaddr) = get_stack_start_aligned() { - // Remove the protection on the guard page. - // FIXME: we cannot unmap the page, because when we mmap() - // above it may be already mapped by the OS, which we can't - // detect from mmap()'s return value. If we unmap this page, - // it will lead to failure growing stack size on platforms like - // macOS. Instead, just restore the page to a writable state. - // This ain't Linux, so we probably don't need to care about - // execstack. - let result = mprotect(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE); - - if result != 0 { - panic!("unable to reset the guard page"); - } - } - } - } - #[cfg(any(target_os = "macos", target_os = "bitrig", target_os = "openbsd", diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 1f9539c36e0..8f8aaa88b22 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -42,10 +42,6 @@ impl Timespec { } } - fn add_duration(&self, other: &Duration) -> Timespec { - self.checked_add_duration(other).expect("overflow when adding duration to time") - } - fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() @@ -68,27 +64,25 @@ impl Timespec { }) } - fn sub_duration(&self, other: &Duration) -> Timespec { + fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { let mut secs = other .as_secs() .try_into() // <- target type would be `libc::time_t` .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs)) - .expect("overflow when subtracting duration from time"); + .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1).expect("overflow when subtracting \ - duration from time"); + secs = secs.checked_sub(1)?; } - Timespec { + Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _, }, - } + }) } } @@ -165,18 +159,16 @@ mod inner { Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t.checked_add(dur2intervals(other)) - .expect("overflow when adding duration to instant"), - } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_add(checked_dur2intervals(other)?)?, + }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { - t: self.t.checked_sub(dur2intervals(other)) - .expect("overflow when subtracting duration from instant"), - } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { + t: self.t.checked_sub(checked_dur2intervals(other)?)?, + }) } } @@ -199,16 +191,12 @@ mod inner { self.t.sub_timespec(&other.t) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.add_duration(other) } - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.t.checked_add_duration(other).map(|t| SystemTime { t }) + Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } @@ -236,12 +224,12 @@ mod inner { } } - fn dur2intervals(dur: &Duration) -> u64 { + fn checked_dur2intervals(dur: &Duration) -> Option<u64> { + let nanos = dur.as_secs() + .checked_mul(NSEC_PER_SEC)? + .checked_add(dur.subsec_nanos() as u64)?; let info = info(); - let nanos = dur.as_secs().checked_mul(NSEC_PER_SEC).and_then(|nanos| { - nanos.checked_add(dur.subsec_nanos() as u64) - }).expect("overflow converting duration to nanoseconds"); - mul_div_u64(nanos, info.denom as u64, info.numer as u64) + Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64)) } fn info() -> &'static libc::mach_timebase_info { @@ -299,12 +287,12 @@ mod inner { }) } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.add_duration(other) } + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant { t: self.t.checked_sub_duration(other)? }) } } @@ -327,16 +315,12 @@ mod inner { self.t.sub_timespec(&other.t) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.add_duration(other) } - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.t.checked_add_duration(other).map(|t| SystemTime { t }) + Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime { t: self.t.sub_duration(other) } + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } diff --git a/src/libstd/sys/unix/weak.rs b/src/libstd/sys/unix/weak.rs index 18944be58ee..7d293f1c47a 100644 --- a/src/libstd/sys/unix/weak.rs +++ b/src/libstd/sys/unix/weak.rs @@ -77,3 +77,38 @@ unsafe fn fetch(name: &str) -> usize { }; libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize } + +#[cfg(not(target_os = "linux"))] +macro_rules! syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name: $t),*) -> $ret { + use libc; + use super::os; + + weak! { fn $name($($t),*) -> $ret } + + if let Some(fun) = $name.get() { + fun($($arg_name),*) + } else { + os::set_errno(libc::ENOSYS); + -1 + } + } + ) +} + +#[cfg(target_os = "linux")] +macro_rules! syscall { + (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => ( + unsafe fn $name($($arg_name:$t),*) -> $ret { + // This looks like a hack, but concat_idents only accepts idents + // (not paths). + use libc::*; + + syscall( + concat_idents!(SYS_, $name), + $($arg_name as c_long),* + ) as $ret + } + ) +} diff --git a/src/libstd/sys/wasm/cmath.rs b/src/libstd/sys/wasm/cmath.rs index 64fc14d42d9..d4f8d66ee3e 100644 --- a/src/libstd/sys/wasm/cmath.rs +++ b/src/libstd/sys/wasm/cmath.rs @@ -8,85 +8,32 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[inline] -pub unsafe fn cbrtf(n: f32) -> f32 { - f64::cbrt(n as f64) as f32 -} - -#[inline] -pub unsafe fn expm1f(n: f32) -> f32 { - f64::exp_m1(n as f64) as f32 -} - -#[inline] -#[allow(deprecated)] -pub unsafe fn fdimf(a: f32, b: f32) -> f32 { - f64::abs_sub(a as f64, b as f64) as f32 -} - -#[inline] -pub unsafe fn log1pf(n: f32) -> f32 { - f64::ln_1p(n as f64) as f32 -} - -#[inline] -pub unsafe fn hypotf(x: f32, y: f32) -> f32 { - f64::hypot(x as f64, y as f64) as f32 -} - -#[inline] -pub unsafe fn acosf(n: f32) -> f32 { - f64::acos(n as f64) as f32 -} - -#[inline] -pub unsafe fn asinf(n: f32) -> f32 { - f64::asin(n as f64) as f32 -} - -#[inline] -pub unsafe fn atan2f(n: f32, b: f32) -> f32 { - f64::atan2(n as f64, b as f64) as f32 -} - -#[inline] -pub unsafe fn atanf(n: f32) -> f32 { - f64::atan(n as f64) as f32 -} - -#[inline] -pub unsafe fn coshf(n: f32) -> f32 { - f64::cosh(n as f64) as f32 -} - -#[inline] -pub unsafe fn sinhf(n: f32) -> f32 { - f64::sinh(n as f64) as f32 -} - -#[inline] -pub unsafe fn tanf(n: f32) -> f32 { - f64::tan(n as f64) as f32 -} - -#[inline] -pub unsafe fn tanhf(n: f32) -> f32 { - f64::tanh(n as f64) as f32 -} - // These symbols are all defined in `compiler-builtins` extern { pub fn acos(n: f64) -> f64; + pub fn acosf(n: f32) -> f32; pub fn asin(n: f64) -> f64; + pub fn asinf(n: f32) -> f32; pub fn atan(n: f64) -> f64; pub fn atan2(a: f64, b: f64) -> f64; + pub fn atan2f(a: f32, b: f32) -> f32; + pub fn atanf(n: f32) -> f32; pub fn cbrt(n: f64) -> f64; + pub fn cbrtf(n: f32) -> f32; pub fn cosh(n: f64) -> f64; + pub fn coshf(n: f32) -> f32; pub fn expm1(n: f64) -> f64; + pub fn expm1f(n: f32) -> f32; pub fn fdim(a: f64, b: f64) -> f64; + pub fn fdimf(a: f32, b: f32) -> f32; + pub fn hypot(x: f64, y: f64) -> f64; + pub fn hypotf(x: f32, y: f32) -> f32; pub fn log1p(n: f64) -> f64; + pub fn log1pf(n: f32) -> f32; pub fn sinh(n: f64) -> f64; + pub fn sinhf(n: f32) -> f32; pub fn tan(n: f64) -> f64; + pub fn tanf(n: f32) -> f32; pub fn tanh(n: f64) -> f64; - pub fn hypot(x: f64, y: f64) -> f64; + pub fn tanhf(n: f32) -> f32; } diff --git a/src/libstd/sys/wasm/env.rs b/src/libstd/sys/wasm/env.rs index 1422042bd02..09235a944ee 100644 --- a/src/libstd/sys/wasm/env.rs +++ b/src/libstd/sys/wasm/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = ""; - pub const OS: &'static str = ""; - pub const DLL_PREFIX: &'static str = ""; - pub const DLL_SUFFIX: &'static str = ".wasm"; - pub const DLL_EXTENSION: &'static str = "wasm"; - pub const EXE_SUFFIX: &'static str = ".wasm"; - pub const EXE_EXTENSION: &'static str = "wasm"; + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".wasm"; + pub const DLL_EXTENSION: &str = "wasm"; + pub const EXE_SUFFIX: &str = ".wasm"; + pub const EXE_EXTENSION: &str = "wasm"; } diff --git a/src/libstd/sys/wasm/net.rs b/src/libstd/sys/wasm/net.rs index 03a5b2d779e..e1c33b09cb4 100644 --- a/src/libstd/sys/wasm/net.rs +++ b/src/libstd/sys/wasm/net.rs @@ -13,11 +13,12 @@ use io; use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; use time::Duration; use sys::{unsupported, Void}; +use convert::TryFrom; pub struct TcpStream(Void); impl TcpStream { - pub fn connect(_: &SocketAddr) -> io::Result<TcpStream> { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { unsupported() } @@ -103,7 +104,7 @@ impl fmt::Debug for TcpStream { pub struct TcpListener(Void); impl TcpListener { - pub fn bind(_: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { unsupported() } @@ -153,7 +154,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(Void); impl UdpSocket { - pub fn bind(_: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { unsupported() } @@ -273,7 +274,7 @@ impl UdpSocket { match self.0 {} } - pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { match self.0 {} } } @@ -286,6 +287,12 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(Void); +impl LookupHost { + pub fn port(&self) -> u16 { + match self.0 {} + } +} + impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option<SocketAddr> { @@ -293,8 +300,20 @@ impl Iterator for LookupHost { } } -pub fn lookup_host(_: &str) -> io::Result<LookupHost> { - unsupported() +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result<LookupHost> { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> { + unsupported() + } } #[allow(nonstandard_style)] diff --git a/src/libstd/sys/wasm/path.rs b/src/libstd/sys/wasm/path.rs index 395b8c1e40e..fcc9d617a87 100644 --- a/src/libstd/sys/wasm/path.rs +++ b/src/libstd/sys/wasm/path.rs @@ -25,5 +25,5 @@ pub fn parse_prefix(_: &OsStr) -> Option<Prefix> { None } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/wasm/stdio.rs b/src/libstd/sys/wasm/stdio.rs index 023f29576a2..e51aba75333 100644 --- a/src/libstd/sys/wasm/stdio.rs +++ b/src/libstd/sys/wasm/stdio.rs @@ -70,6 +70,10 @@ pub fn is_ebadf(_err: &io::Error) -> bool { true } -pub fn stderr_prints_nothing() -> bool { - !cfg!(feature = "wasm_syscall") +pub fn panic_output() -> Option<impl io::Write> { + if cfg!(feature = "wasm_syscall") { + Stderr::new().ok() + } else { + None + } } diff --git a/src/libstd/sys/wasm/thread.rs b/src/libstd/sys/wasm/thread.rs index 4ad89c42b92..f9abb0b825a 100644 --- a/src/libstd/sys/wasm/thread.rs +++ b/src/libstd/sys/wasm/thread.rs @@ -19,7 +19,8 @@ pub struct Thread(Void); pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; impl Thread { - pub unsafe fn new<'a>(_stack: usize, _p: Box<dyn FnBox() + 'a>) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(_stack: usize, _p: Box<dyn FnBox()>) -> io::Result<Thread> { unsupported() @@ -67,7 +68,6 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } cfg_if! { diff --git a/src/libstd/sys/wasm/time.rs b/src/libstd/sys/wasm/time.rs index 991e8176edf..cc56773e0ea 100644 --- a/src/libstd/sys/wasm/time.rs +++ b/src/libstd/sys/wasm/time.rs @@ -28,12 +28,12 @@ impl Instant { self.0 - other.0 } - pub fn add_duration(&self, other: &Duration) -> Instant { - Instant(self.0 + *other) + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_add(*other)?)) } - pub fn sub_duration(&self, other: &Duration) -> Instant { - Instant(self.0 - *other) + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { + Some(Instant(self.0.checked_sub(*other)?)) } } @@ -47,15 +47,11 @@ impl SystemTime { self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - SystemTime(self.0 + *other) - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - self.0.checked_add(*other).map(|d| SystemTime(d)) + Some(SystemTime(self.0.checked_add(*other)?)) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - SystemTime(self.0 - *other) + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + Some(SystemTime(self.0.checked_sub(*other)?)) } } diff --git a/src/libstd/sys/windows/args.rs b/src/libstd/sys/windows/args.rs index 4784633edc1..9e9198e05ee 100644 --- a/src/libstd/sys/windows/args.rs +++ b/src/libstd/sys/windows/args.rs @@ -11,12 +11,14 @@ #![allow(dead_code)] // runtime init functions not used during testing use os::windows::prelude::*; +use sys::windows::os::current_exe; use sys::c; -use slice; -use ops::Range; use ffi::OsString; -use libc::{c_int, c_void}; use fmt; +use vec; +use core::iter; +use slice; +use path::PathBuf; pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } @@ -24,20 +26,146 @@ pub unsafe fn cleanup() { } pub fn args() -> Args { unsafe { - let mut nArgs: c_int = 0; - let lpCmdLine = c::GetCommandLineW(); - let szArgList = c::CommandLineToArgvW(lpCmdLine, &mut nArgs); - - // szArcList can be NULL if CommandLinToArgvW failed, - // but in that case nArgs is 0 so we won't actually - // try to read a null pointer - Args { cur: szArgList, range: 0..(nArgs as isize) } + let lp_cmd_line = c::GetCommandLineW(); + let parsed_args_list = parse_lp_cmd_line( + lp_cmd_line as *const u16, + || current_exe().map(PathBuf::into_os_string).unwrap_or_else(|_| OsString::new())); + + Args { parsed_args_list: parsed_args_list.into_iter() } } } +/// Implements the Windows command-line argument parsing algorithm. +/// +/// Microsoft's documentation for the Windows CLI argument format can be found at +/// <https://docs.microsoft.com/en-us/previous-versions//17w5ykft(v=vs.85)>. +/// +/// Windows includes a function to do this in shell32.dll, +/// but linking with that DLL causes the process to be registered as a GUI application. +/// GUI applications add a bunch of overhead, even if no windows are drawn. See +/// <https://randomascii.wordpress.com/2018/12/03/a-not-called-function-can-cause-a-5x-slowdown/>. +/// +/// This function was tested for equivalence to the shell32.dll implementation in +/// Windows 10 Pro v1803, using an exhaustive test suite available at +/// <https://gist.github.com/notriddle/dde431930c392e428055b2dc22e638f5> or +/// <https://paste.gg/p/anonymous/47d6ed5f5bd549168b1c69c799825223>. +unsafe fn parse_lp_cmd_line<F: Fn() -> OsString>(lp_cmd_line: *const u16, exe_name: F) + -> Vec<OsString> { + const BACKSLASH: u16 = '\\' as u16; + const QUOTE: u16 = '"' as u16; + const TAB: u16 = '\t' as u16; + const SPACE: u16 = ' ' as u16; + let mut ret_val = Vec::new(); + if lp_cmd_line.is_null() || *lp_cmd_line == 0 { + ret_val.push(exe_name()); + return ret_val; + } + let mut cmd_line = { + let mut end = 0; + while *lp_cmd_line.offset(end) != 0 { + end += 1; + } + slice::from_raw_parts(lp_cmd_line, end as usize) + }; + // The executable name at the beginning is special. + cmd_line = match cmd_line[0] { + // The executable name ends at the next quote mark, + // no matter what. + QUOTE => { + let args = { + let mut cut = cmd_line[1..].splitn(2, |&c| c == QUOTE); + if let Some(exe) = cut.next() { + ret_val.push(OsString::from_wide(exe)); + } + cut.next() + }; + if let Some(args) = args { + args + } else { + return ret_val; + } + } + // Implement quirk: when they say whitespace here, + // they include the entire ASCII control plane: + // "However, if lpCmdLine starts with any amount of whitespace, CommandLineToArgvW + // will consider the first argument to be an empty string. Excess whitespace at the + // end of lpCmdLine is ignored." + 0...SPACE => { + ret_val.push(OsString::new()); + &cmd_line[1..] + }, + // The executable name ends at the next whitespace, + // no matter what. + _ => { + let args = { + let mut cut = cmd_line.splitn(2, |&c| c > 0 && c <= SPACE); + if let Some(exe) = cut.next() { + ret_val.push(OsString::from_wide(exe)); + } + cut.next() + }; + if let Some(args) = args { + args + } else { + return ret_val; + } + } + }; + let mut cur = Vec::new(); + let mut in_quotes = false; + let mut was_in_quotes = false; + let mut backslash_count: usize = 0; + for &c in cmd_line { + match c { + // backslash + BACKSLASH => { + backslash_count += 1; + was_in_quotes = false; + }, + QUOTE if backslash_count % 2 == 0 => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); + backslash_count = 0; + if was_in_quotes { + cur.push('"' as u16); + was_in_quotes = false; + } else { + was_in_quotes = in_quotes; + in_quotes = !in_quotes; + } + } + QUOTE if backslash_count % 2 != 0 => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count / 2)); + backslash_count = 0; + was_in_quotes = false; + cur.push(b'"' as u16); + } + SPACE | TAB if !in_quotes => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + if !cur.is_empty() || was_in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + cur.truncate(0); + } + backslash_count = 0; + was_in_quotes = false; + } + _ => { + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + backslash_count = 0; + was_in_quotes = false; + cur.push(c); + } + } + } + cur.extend(iter::repeat(b'\\' as u16).take(backslash_count)); + // include empty quoted strings at the end of the arguments list + if !cur.is_empty() || was_in_quotes || in_quotes { + ret_val.push(OsString::from_wide(&cur[..])); + } + ret_val +} + pub struct Args { - range: Range<isize>, - cur: *mut *mut u16, + parsed_args_list: vec::IntoIter<OsString>, } pub struct ArgsInnerDebug<'a> { @@ -46,19 +174,7 @@ pub struct ArgsInnerDebug<'a> { impl<'a> fmt::Debug for ArgsInnerDebug<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("[")?; - let mut first = true; - for i in self.args.range.clone() { - if !first { - f.write_str(", ")?; - } - first = false; - - // Here we do allocation which could be avoided. - fmt::Debug::fmt(&unsafe { os_string_from_ptr(*self.args.cur.offset(i)) }, f)?; - } - f.write_str("]")?; - Ok(()) + self.args.parsed_args_list.as_slice().fmt(f) } } @@ -70,38 +186,82 @@ impl Args { } } -unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString { - let mut len = 0; - while *ptr.offset(len) != 0 { len += 1; } - - // Push it onto the list. - let ptr = ptr as *const u16; - let buf = slice::from_raw_parts(ptr, len as usize); - OsStringExt::from_wide(buf) -} - impl Iterator for Args { type Item = OsString; - fn next(&mut self) -> Option<OsString> { - self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) - } - fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } + fn next(&mut self) -> Option<OsString> { self.parsed_args_list.next() } + fn size_hint(&self) -> (usize, Option<usize>) { self.parsed_args_list.size_hint() } } impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option<OsString> { - self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) - } + fn next_back(&mut self) -> Option<OsString> { self.parsed_args_list.next_back() } } impl ExactSizeIterator for Args { - fn len(&self) -> usize { self.range.len() } + fn len(&self) -> usize { self.parsed_args_list.len() } } -impl Drop for Args { - fn drop(&mut self) { - // self.cur can be null if CommandLineToArgvW previously failed, - // but LocalFree ignores NULL pointers - unsafe { c::LocalFree(self.cur as *mut c_void); } +#[cfg(test)] +mod tests { + use sys::windows::args::*; + use ffi::OsString; + + fn chk(string: &str, parts: &[&str]) { + let mut wide: Vec<u16> = OsString::from(string).encode_wide().collect(); + wide.push(0); + let parsed = unsafe { + parse_lp_cmd_line(wide.as_ptr() as *const u16, || OsString::from("TEST.EXE")) + }; + let expected: Vec<OsString> = parts.iter().map(|k| OsString::from(k)).collect(); + assert_eq!(parsed.as_slice(), expected.as_slice()); + } + + #[test] + fn empty() { + chk("", &["TEST.EXE"]); + chk("\0", &["TEST.EXE"]); + } + + #[test] + fn single_words() { + chk("EXE one_word", &["EXE", "one_word"]); + chk("EXE a", &["EXE", "a"]); + chk("EXE 😅", &["EXE", "😅"]); + chk("EXE 😅🤦", &["EXE", "😅🤦"]); + } + + #[test] + fn official_examples() { + chk(r#"EXE "abc" d e"#, &["EXE", "abc", "d", "e"]); + chk(r#"EXE a\\\b d"e f"g h"#, &["EXE", r#"a\\\b"#, "de fg", "h"]); + chk(r#"EXE a\\\"b c d"#, &["EXE", r#"a\"b"#, "c", "d"]); + chk(r#"EXE a\\\\"b c" d e"#, &["EXE", r#"a\\b c"#, "d", "e"]); + } + + #[test] + fn whitespace_behavior() { + chk(r#" test"#, &["", "test"]); + chk(r#" test"#, &["", "test"]); + chk(r#" test test2"#, &["", "test", "test2"]); + chk(r#" test test2"#, &["", "test", "test2"]); + chk(r#"test test2 "#, &["test", "test2"]); + chk(r#"test test2 "#, &["test", "test2"]); + chk(r#"test "#, &["test"]); + } + + #[test] + fn genius_quotes() { + chk(r#"EXE "" """#, &["EXE", "", ""]); + chk(r#"EXE "" """"#, &["EXE", "", "\""]); + chk( + r#"EXE "this is """all""" in the same argument""#, + &["EXE", "this is \"all\" in the same argument"] + ); + chk(r#"EXE "a"""#, &["EXE", "a\""]); + chk(r#"EXE "a"" a"#, &["EXE", "a\"", "a"]); + // quotes cannot be escaped in command names + chk(r#""EXE" check"#, &["EXE", "check"]); + chk(r#""EXE check""#, &["EXE check"]); + chk(r#""EXE """for""" check"#, &["EXE ", r#"for""#, "check"]); + chk(r#""EXE \"for\" check"#, &[r#"EXE \"#, r#"for""#, "check"]); } } diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index c84874a3e88..fa21f459a8a 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -1035,9 +1035,6 @@ extern "system" { pub fn SetLastError(dwErrCode: DWORD); pub fn GetCommandLineW() -> *mut LPCWSTR; - pub fn LocalFree(ptr: *mut c_void); - pub fn CommandLineToArgvW(lpCmdLine: *mut LPCWSTR, - pNumArgs: *mut c_int) -> *mut *mut u16; pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD; pub fn OpenProcessToken(ProcessHandle: HANDLE, diff --git a/src/libstd/sys/windows/env.rs b/src/libstd/sys/windows/env.rs index e6d74895774..4523df04f24 100644 --- a/src/libstd/sys/windows/env.rs +++ b/src/libstd/sys/windows/env.rs @@ -9,11 +9,11 @@ // except according to those terms. pub mod os { - pub const FAMILY: &'static str = "windows"; - pub const OS: &'static str = "windows"; - pub const DLL_PREFIX: &'static str = ""; - pub const DLL_SUFFIX: &'static str = ".dll"; - pub const DLL_EXTENSION: &'static str = "dll"; - pub const EXE_SUFFIX: &'static str = ".exe"; - pub const EXE_EXTENSION: &'static str = "exe"; + pub const FAMILY: &str = "windows"; + pub const OS: &str = "windows"; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".dll"; + pub const DLL_EXTENSION: &str = "dll"; + pub const EXE_SUFFIX: &str = ".exe"; + pub const EXE_EXTENSION: &str = "exe"; } diff --git a/src/libstd/sys/windows/ext/ffi.rs b/src/libstd/sys/windows/ext/ffi.rs index bae0d02786a..0a13aeabe84 100644 --- a/src/libstd/sys/windows/ext/ffi.rs +++ b/src/libstd/sys/windows/ext/ffi.rs @@ -117,7 +117,7 @@ impl OsStringExt for OsString { /// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html #[stable(feature = "rust1", since = "1.0.0")] pub trait OsStrExt { - /// Re-encodes an `OsStr` as a wide character sequence, i.e. potentially + /// Re-encodes an `OsStr` as a wide character sequence, i.e., potentially /// ill-formed UTF-16. /// /// This is lossless: calling [`OsString::from_wide`] and then diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 082d4689c7b..949060b34dd 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -443,7 +443,7 @@ impl FromInner<c::HANDLE> for File { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // FIXME(#24570): add more info here (e.g. mode) + // FIXME(#24570): add more info here (e.g., mode) let mut b = f.debug_struct("File"); b.field("handle", &self.handle.raw()); if let Ok(path) = get_path(&self) { diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index 29ea82c2053..84ef62e5fe9 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -48,8 +48,8 @@ pub fn error_string(mut errnum: i32) -> String { // `[MS-ERREF]`: https://msdn.microsoft.com/en-us/library/cc231198.aspx if (errnum & c::FACILITY_NT_BIT as i32) != 0 { // format according to https://support.microsoft.com/en-us/help/259693 - const NTDLL_DLL: &'static [u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, - '.' as _, 'D' as _, 'L' as _, 'L' as _, 0]; + const NTDLL_DLL: &[u16] = &['N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, + '.' as _, 'D' as _, 'L' as _, 'L' as _, 0]; module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); if module != ptr::null_mut() { @@ -67,7 +67,7 @@ pub fn error_string(mut errnum: i32) -> String { buf.len() as c::DWORD, ptr::null()) as usize; if res == 0 { - // Sometimes FormatMessageW can fail e.g. system doesn't like langId, + // Sometimes FormatMessageW can fail e.g., system doesn't like langId, let fm_err = errno(); return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); @@ -76,7 +76,7 @@ pub fn error_string(mut errnum: i32) -> String { match String::from_utf16(&buf[..res]) { Ok(mut msg) => { // Trim trailing CRLF inserted by FormatMessageW - let len = msg.trim_right().len(); + let len = msg.trim_end().len(); msg.truncate(len); msg }, diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index 98d62a0c953..385ea8e0531 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -91,10 +91,7 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { } fn parse_two_comps(mut path: &[u8], f: fn(u8) -> bool) -> Option<(&[u8], &[u8])> { - let first = match path.iter().position(|x| f(*x)) { - None => return None, - Some(x) => &path[..x], - }; + let first = &path[..path.iter().position(|x| f(*x))?]; path = &path[(first.len() + 1)..]; let idx = path.iter().position(|x| f(*x)); let second = &path[..idx.unwrap_or(path.len())]; @@ -102,5 +99,5 @@ pub fn parse_prefix<'a>(path: &'a OsStr) -> Option<Prefix> { } } -pub const MAIN_SEP_STR: &'static str = "\\"; +pub const MAIN_SEP_STR: &str = "\\"; pub const MAIN_SEP: char = '\\'; diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 4b19519a57a..f9eed31f0e0 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -100,23 +100,23 @@ pub fn anon_pipe(ours_readable: bool) -> io::Result<Pipes> { 0, ptr::null_mut()); - // We pass the FILE_FLAG_FIRST_PIPE_INSTANCE flag above, and we're + // We pass the `FILE_FLAG_FIRST_PIPE_INSTANCE` flag above, and we're // also just doing a best effort at selecting a unique name. If - // ERROR_ACCESS_DENIED is returned then it could mean that we + // `ERROR_ACCESS_DENIED` is returned then it could mean that we // accidentally conflicted with an already existing pipe, so we try // again. // // Don't try again too much though as this could also perhaps be a // legit error. - // If ERROR_INVALID_PARAMETER is returned, this probably means we're - // running on pre-Vista version where PIPE_REJECT_REMOTE_CLIENTS is + // If `ERROR_INVALID_PARAMETER` is returned, this probably means we're + // running on pre-Vista version where `PIPE_REJECT_REMOTE_CLIENTS` is // not supported, so we continue retrying without it. This implies // reduced security on Windows versions older than Vista by allowing // connections to this pipe from remote machines. // Proper fix would increase the number of FFI imports and introduce // significant amount of Windows XP specific code with no clean // testing strategy - // for more info see https://github.com/rust-lang/rust/pull/37677 + // For more info, see https://github.com/rust-lang/rust/pull/37677. if handle == c::INVALID_HANDLE_VALUE { let err = io::Error::last_os_error(); let raw_os_err = err.raw_os_error(); diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index ff1ee0d26fe..03c1bb54af8 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -487,7 +487,7 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> { } else { if x == '"' as u16 { // Add n+1 backslashes to total 2n+1 before internal '"'. - cmd.extend((0..(backslashes + 1)).map(|_| '\\' as u16)); + cmd.extend((0..=backslashes).map(|_| '\\' as u16)); } backslashes = 0; } diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs index c3a94698a0f..61e0db87ebe 100644 --- a/src/libstd/sys/windows/stdio.rs +++ b/src/libstd/sys/windows/stdio.rs @@ -228,6 +228,6 @@ pub fn is_ebadf(err: &io::Error) -> bool { // been seen to be acceptable. pub const STDIN_BUF_SIZE: usize = 8 * 1024; -pub fn stderr_prints_nothing() -> bool { - false +pub fn panic_output() -> Option<impl io::Write> { + Stderr::new().ok() } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 85588cc6c8e..621ae2fda58 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -28,7 +28,8 @@ pub struct Thread { } impl Thread { - pub unsafe fn new<'a>(stack: usize, p: Box<dyn FnBox() + 'a>) + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new(stack: usize, p: Box<dyn FnBox()>) -> io::Result<Thread> { let p = box p; @@ -97,5 +98,4 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option<Guard> { None } pub unsafe fn init() -> Option<Guard> { None } - pub unsafe fn deinit() {} } diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs index c809a0b98ac..bb2c97ea149 100644 --- a/src/libstd/sys/windows/time.rs +++ b/src/libstd/sys/windows/time.rs @@ -68,30 +68,27 @@ impl Instant { Duration::new(nanos / NANOS_PER_SEC, (nanos % NANOS_PER_SEC) as u32) } - pub fn add_duration(&self, other: &Duration) -> Instant { + pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> { let freq = frequency() as u64; - let t = other.as_secs().checked_mul(freq).and_then(|i| { - (self.t as u64).checked_add(i) - }).and_then(|i| { - i.checked_add(mul_div_u64(other.subsec_nanos() as u64, freq, - NANOS_PER_SEC)) - }).expect("overflow when adding duration to time"); - Instant { + let t = other.as_secs() + .checked_mul(freq)? + .checked_add(mul_div_u64(other.subsec_nanos() as u64, freq, NANOS_PER_SEC))? + .checked_add(self.t as u64)?; + Some(Instant { t: t as c::LARGE_INTEGER, - } + }) } - pub fn sub_duration(&self, other: &Duration) -> Instant { + pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> { let freq = frequency() as u64; let t = other.as_secs().checked_mul(freq).and_then(|i| { (self.t as u64).checked_sub(i) }).and_then(|i| { - i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq, - NANOS_PER_SEC)) - }).expect("overflow when subtracting duration from time"); - Instant { + i.checked_sub(mul_div_u64(other.subsec_nanos() as u64, freq, NANOS_PER_SEC)) + })?; + Some(Instant { t: t as c::LARGE_INTEGER, - } + }) } } @@ -127,20 +124,14 @@ impl SystemTime { } } - pub fn add_duration(&self, other: &Duration) -> SystemTime { - self.checked_add_duration(other).expect("overflow when adding duration to time") - } - pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> { - checked_dur2intervals(other) - .and_then(|d| self.intervals().checked_add(d)) - .map(|i| SystemTime::from_intervals(i)) + let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?; + Some(SystemTime::from_intervals(intervals)) } - pub fn sub_duration(&self, other: &Duration) -> SystemTime { - let intervals = self.intervals().checked_sub(dur2intervals(other)) - .expect("overflow when subtracting from time"); - SystemTime::from_intervals(intervals) + pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> { + let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?; + Some(SystemTime::from_intervals(intervals)) } } @@ -184,16 +175,12 @@ impl Hash for SystemTime { } } -fn checked_dur2intervals(d: &Duration) -> Option<i64> { - d.as_secs() - .checked_mul(INTERVALS_PER_SEC) - .and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100)) - .and_then(|i| i.try_into().ok()) -} - -fn dur2intervals(d: &Duration) -> i64 { - checked_dur2intervals(d) - .expect("overflow when converting duration to intervals") +fn checked_dur2intervals(dur: &Duration) -> Option<i64> { + dur.as_secs() + .checked_mul(INTERVALS_PER_SEC)? + .checked_add(dur.subsec_nanos() as u64 / 100)? + .try_into() + .ok() } fn intervals2dur(intervals: u64) -> Duration { diff --git a/src/libstd/sys_common/backtrace.rs b/src/libstd/sys_common/backtrace.rs index 77371782977..e44113f76f4 100644 --- a/src/libstd/sys_common/backtrace.rs +++ b/src/libstd/sys_common/backtrace.rs @@ -14,11 +14,12 @@ use env; use io::prelude::*; use io; +use path::{self, Path}; +use ptr; +use rustc_demangle::demangle; use str; use sync::atomic::{self, Ordering}; -use path::{self, Path}; use sys::mutex::Mutex; -use ptr; pub use sys::backtrace::{ unwind_backtrace, @@ -191,7 +192,14 @@ fn output(w: &mut dyn Write, idx: usize, frame: Frame, PrintFormat::Short => write!(w, " {:2}: ", idx)?, } match s { - Some(string) => demangle(w, string, format)?, + Some(string) => { + let symbol = demangle(string); + match format { + PrintFormat::Full => write!(w, "{}", symbol)?, + // strip the trailing hash if short mode + PrintFormat::Short => write!(w, "{:#}", symbol)?, + } + } None => w.write_all(b"<unknown>")?, } w.write_all(b"\n") @@ -235,228 +243,3 @@ fn output_fileline(w: &mut dyn Write, w.write_all(b"\n") } - -// All rust symbols are in theory lists of "::"-separated identifiers. Some -// assemblers, however, can't handle these characters in symbol names. To get -// around this, we use C++-style mangling. The mangling method is: -// -// 1. Prefix the symbol with "_ZN" -// 2. For each element of the path, emit the length plus the element -// 3. End the path with "E" -// -// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". -// -// We're the ones printing our backtraces, so we can't rely on anything else to -// demangle our symbols. It's *much* nicer to look at demangled symbols, so -// this function is implemented to give us nice pretty output. -// -// Note that this demangler isn't quite as fancy as it could be. We have lots -// of other information in our symbols like hashes, version, type information, -// etc. Additionally, this doesn't handle glue symbols at all. -pub fn demangle(writer: &mut dyn Write, mut s: &str, format: PrintFormat) -> io::Result<()> { - // During ThinLTO LLVM may import and rename internal symbols, so strip out - // those endings first as they're one of the last manglings applied to - // symbol names. - let llvm = ".llvm."; - if let Some(i) = s.find(llvm) { - let candidate = &s[i + llvm.len()..]; - let all_hex = candidate.chars().all(|c| { - match c { - 'A' ..= 'F' | '0' ..= '9' => true, - _ => false, - } - }); - - if all_hex { - s = &s[..i]; - } - } - - // Validate the symbol. If it doesn't look like anything we're - // expecting, we just print it literally. Note that we must handle non-rust - // symbols because we could have any function in the backtrace. - let mut valid = true; - let mut inner = s; - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { - inner = &s[3 .. s.len() - 1]; - // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. - } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { - inner = &s[2 .. s.len() - 1]; - } else { - valid = false; - } - - if valid { - let mut chars = inner.chars(); - while valid { - let mut i = 0; - for c in chars.by_ref() { - if c.is_numeric() { - i = i * 10 + c as usize - '0' as usize; - } else { - break - } - } - if i == 0 { - valid = chars.next().is_none(); - break - } else if chars.by_ref().take(i - 1).count() != i - 1 { - valid = false; - } - } - } - - // Alright, let's do this. - if !valid { - writer.write_all(s.as_bytes())?; - } else { - // remove the `::hfc2edb670e5eda97` part at the end of the symbol. - if format == PrintFormat::Short { - // The symbol in still mangled. - let mut split = inner.rsplitn(2, "17h"); - match (split.next(), split.next()) { - (Some(addr), rest) => { - if addr.len() == 16 && - addr.chars().all(|c| c.is_digit(16)) - { - inner = rest.unwrap_or(""); - } - } - _ => (), - } - } - - let mut first = true; - while !inner.is_empty() { - if !first { - writer.write_all(b"::")?; - } else { - first = false; - } - let mut rest = inner; - while rest.chars().next().unwrap().is_numeric() { - rest = &rest[1..]; - } - let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap(); - inner = &rest[i..]; - rest = &rest[..i]; - if rest.starts_with("_$") { - rest = &rest[1..]; - } - while !rest.is_empty() { - if rest.starts_with(".") { - if let Some('.') = rest[1..].chars().next() { - writer.write_all(b"::")?; - rest = &rest[2..]; - } else { - writer.write_all(b".")?; - rest = &rest[1..]; - } - } else if rest.starts_with("$") { - macro_rules! demangle { - ($($pat:expr => $demangled:expr),*) => ({ - $(if rest.starts_with($pat) { - writer.write_all($demangled)?; - rest = &rest[$pat.len()..]; - } else)* - { - writer.write_all(rest.as_bytes())?; - break; - } - - }) - } - - // see src/librustc/back/link.rs for these mappings - demangle! ( - "$SP$" => b"@", - "$BP$" => b"*", - "$RF$" => b"&", - "$LT$" => b"<", - "$GT$" => b">", - "$LP$" => b"(", - "$RP$" => b")", - "$C$" => b",", - - // in theory we can demangle any Unicode code point, but - // for simplicity we just catch the common ones. - "$u7e$" => b"~", - "$u20$" => b" ", - "$u27$" => b"'", - "$u5b$" => b"[", - "$u5d$" => b"]", - "$u7b$" => b"{", - "$u7d$" => b"}", - "$u3b$" => b";", - "$u2b$" => b"+", - "$u22$" => b"\"" - ) - } else { - let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') { - None => rest.len(), - Some((i, _)) => i, - }; - writer.write_all(rest[..idx].as_bytes())?; - rest = &rest[idx..]; - } - } - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use sys_common; - macro_rules! t { ($a:expr, $b:expr) => ({ - let mut m = Vec::new(); - sys_common::backtrace::demangle(&mut m, - $a, - super::PrintFormat::Full).unwrap(); - assert_eq!(String::from_utf8(m).unwrap(), $b); - }) } - - #[test] - fn demangle() { - t!("test", "test"); - t!("_ZN4testE", "test"); - t!("_ZN4test", "_ZN4test"); - t!("_ZN4test1a2bcE", "test::a::bc"); - } - - #[test] - fn demangle_dollars() { - t!("_ZN4$RP$E", ")"); - t!("_ZN8$RF$testE", "&test"); - t!("_ZN8$BP$test4foobE", "*test::foob"); - t!("_ZN9$u20$test4foobE", " test::foob"); - t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>"); - } - - #[test] - fn demangle_many_dollars() { - t!("_ZN13test$u20$test4foobE", "test test::foob"); - t!("_ZN12test$BP$test4foobE", "test*test::foob"); - } - - #[test] - fn demangle_windows() { - t!("ZN4testE", "test"); - t!("ZN13test$u20$test4foobE", "test test::foob"); - t!("ZN12test$RF$test4foobE", "test&test::foob"); - } - - #[test] - fn demangle_elements_beginning_with_underscore() { - t!("_ZN13_$LT$test$GT$E", "<test>"); - t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}"); - t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR"); - } - - #[test] - fn demangle_trait_impls() { - t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", - "<Test + 'static as foo::Bar<Test>>::bar"); - } -} diff --git a/src/libstd/sys_common/gnu/libbacktrace.rs b/src/libstd/sys_common/gnu/libbacktrace.rs index 6ad3af6aee1..c2589d477ee 100644 --- a/src/libstd/sys_common/gnu/libbacktrace.rs +++ b/src/libstd/sys_common/gnu/libbacktrace.rs @@ -23,7 +23,7 @@ pub fn foreach_symbol_fileline<F>(frame: Frame, where F: FnMut(&[u8], u32) -> io::Result<()> { // pcinfo may return an arbitrary number of file:line pairs, - // in the order of stack trace (i.e. inlined calls first). + // in the order of stack trace (i.e., inlined calls first). // in order to avoid allocation, we stack-allocate a fixed size of entries. const FILELINE_SIZE: usize = 32; let mut fileline_buf = [(ptr::null(), !0); FILELINE_SIZE]; diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index 4b8cde3d1f4..881794d9f16 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -57,9 +57,11 @@ pub mod bytestring; pub mod process; cfg_if! { - if #[cfg(any(target_os = "cloudabi", target_os = "l4re", target_os = "redox"))] { - pub use sys::net; - } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { + if #[cfg(any(target_os = "cloudabi", + target_os = "l4re", + target_os = "redox", + all(target_arch = "wasm32", not(target_os = "emscripten")), + target_env = "sgx"))] { pub use sys::net; } else { pub mod net; diff --git a/src/libstd/sys_common/net.rs b/src/libstd/sys_common/net.rs index d09a233ed89..dce2bf71cec 100644 --- a/src/libstd/sys_common/net.rs +++ b/src/libstd/sys_common/net.rs @@ -20,6 +20,7 @@ use sys::net::{cvt, cvt_r, cvt_gai, Socket, init, wrlen_t}; use sys::net::netc as c; use sys_common::{AsInner, FromInner, IntoInner}; use time::Duration; +use convert::{TryFrom, TryInto}; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", @@ -129,6 +130,13 @@ fn to_ipv6mr_interface(value: u32) -> ::libc::c_uint { pub struct LookupHost { original: *mut c::addrinfo, cur: *mut c::addrinfo, + port: u16 +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } } impl Iterator for LookupHost { @@ -158,17 +166,45 @@ impl Drop for LookupHost { } } -pub fn lookup_host(host: &str) -> io::Result<LookupHost> { - init(); +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; - let c_host = CString::new(host)?; - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| { - LookupHost { original: res, cur: res } - }) + fn try_from(s: &str) -> io::Result<LookupHost> { + macro_rules! try_opt { + ($e:expr, $msg:expr) => ( + match $e { + Some(r) => r, + None => return Err(io::Error::new(io::ErrorKind::InvalidInput, + $msg)), + } + ) + } + + // split the string by ':' and convert the second part to u16 + let mut parts_iter = s.rsplitn(2, ':'); + let port_str = try_opt!(parts_iter.next(), "invalid socket address"); + let host = try_opt!(parts_iter.next(), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result<LookupHost> { + init(); + + let c_host = CString::new(host)?; + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)).map(|_| { + LookupHost { original: res, cur: res, port } + }) + } } } @@ -181,7 +217,9 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(addr: &SocketAddr) -> io::Result<TcpStream> { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + let addr = addr?; + init(); let sock = Socket::new(addr, c::SOCK_STREAM)?; @@ -317,7 +355,9 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(addr: &SocketAddr) -> io::Result<TcpListener> { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + let addr = addr?; + init(); let sock = Socket::new(addr, c::SOCK_STREAM)?; @@ -418,7 +458,9 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(addr: &SocketAddr) -> io::Result<UdpSocket> { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + let addr = addr?; + init(); let sock = Socket::new(addr, c::SOCK_DGRAM)?; @@ -584,8 +626,8 @@ impl UdpSocket { Ok(ret as usize) } - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addrp, len) = addr.into_inner(); + pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { + let (addrp, len) = addr?.into_inner(); cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(|_| ()) } } @@ -618,7 +660,7 @@ mod tests { #[test] fn no_lookup_host_duplicates() { let mut addrs = HashMap::new(); - let lh = match lookup_host("localhost") { + let lh = match LookupHost::try_from(("localhost", 0)) { Ok(lh) => lh, Err(e) => panic!("couldn't resolve `localhost': {}", e) }; diff --git a/src/libstd/sys_common/util.rs b/src/libstd/sys_common/util.rs index a373e980b97..fc86a59d17f 100644 --- a/src/libstd/sys_common/util.rs +++ b/src/libstd/sys_common/util.rs @@ -10,14 +10,13 @@ use fmt; use io::prelude::*; -use sys::stdio::{Stderr, stderr_prints_nothing}; +use sys::stdio::panic_output; use thread; pub fn dumb_print(args: fmt::Arguments) { - if stderr_prints_nothing() { - return + if let Some(mut out) = panic_output() { + let _ = out.write_fmt(args); } - let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); } // Other platforms should use the appropriate platform-specific mechanism for diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 19ce932aa12..3530b7af9ad 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -40,7 +40,7 @@ use str; use sync::Arc; use sys_common::AsInner; -const UTF8_REPLACEMENT_CHARACTER: &'static str = "\u{FFFD}"; +const UTF8_REPLACEMENT_CHARACTER: &str = "\u{FFFD}"; /// A Unicode code point: from U+0000 to U+10FFFF. /// diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 4df47511172..8bb99096061 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -269,7 +269,7 @@ impl<T: 'static> LocalKey<T> { // ptr::write(ptr, Some(value)) // // Due to this pattern it's possible for the destructor of the value in - // `ptr` (e.g. if this is being recursively initialized) to re-access + // `ptr` (e.g., if this is being recursively initialized) to re-access // TLS, in which case there will be a `&` and `&mut` pointer to the same // value (an aliasing violation). To avoid setting the "I'm running a // destructor" flag we just use `mem::replace` which should sequence the diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 3a9f3ec5c6f..b70dfc46af5 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -93,7 +93,7 @@ //! Threads are represented via the [`Thread`] type, which you can get in one of //! two ways: //! -//! * By spawning a new thread, e.g. using the [`thread::spawn`][`spawn`] +//! * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] //! function, and calling [`thread`][`JoinHandle::thread`] on the [`JoinHandle`]. //! * By requesting the current thread, using the [`thread::current`] function. //! @@ -124,7 +124,7 @@ //! thread, use [`Thread::name`]. A couple examples of where the name of a thread gets used: //! //! * If a panic occurs in a named thread, the thread name will be printed in the panic message. -//! * The thread name is provided to the OS where applicable (e.g. `pthread_setname_np` in +//! * The thread name is provided to the OS where applicable (e.g., `pthread_setname_np` in //! unix-like platforms). //! //! ## Stack size @@ -167,10 +167,12 @@ #![stable(feature = "rust1", since = "1.0.0")] use any::Any; +use boxed::FnBox; use cell::UnsafeCell; use ffi::{CStr, CString}; use fmt; use io; +use mem; use panic; use panicking; use str; @@ -420,7 +422,7 @@ impl Builder { /// /// - ensure that [`join`][`JoinHandle::join`] is called before any referenced /// data is dropped - /// - use only types with `'static` lifetime bounds, i.e. those with no or only + /// - use only types with `'static` lifetime bounds, i.e., those with no or only /// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`] /// and [`thread::spawn`][`spawn`] enforce this property statically) /// @@ -452,8 +454,8 @@ impl Builder { /// [`io::Result`]: ../../std/io/type.Result.html /// [`JoinHandle`]: ../../std/thread/struct.JoinHandle.html #[unstable(feature = "thread_spawn_unchecked", issue = "55132")] - pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>> where - F: FnOnce() -> T, F: Send, T: Send + pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>> where + F: FnOnce() -> T, F: Send + 'a, T: Send + 'a { let Builder { name, stack_size } = self; @@ -482,7 +484,21 @@ impl Builder { }; Ok(JoinHandle(JoinInner { - native: Some(imp::Thread::new(stack_size, Box::new(main))?), + // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed + // through FFI or otherwise used with low-level threading primitives that have no + // notion of or way to enforce lifetimes. + // + // As mentioned in the `Safety` section of this function's documentation, the caller of + // this function needs to guarantee that the passed-in lifetime is sufficiently long + // for the lifetime of the thread. + // + // Similarly, the `sys` implementation must guarantee that no references to the closure + // exist after the thread has terminated, which is signaled by `Thread::join` + // returning. + native: Some(imp::Thread::new( + stack_size, + mem::transmute::<Box<dyn FnBox() + 'a>, Box<dyn FnBox() + 'static>>(Box::new(main)) + )?), thread: my_thread, packet: Packet(my_packet), })) @@ -676,7 +692,7 @@ pub fn yield_now() { /// already poison themselves when a thread panics while holding the lock. /// /// This can also be used in multithreaded applications, in order to send a -/// message to other threads warning that a thread has panicked (e.g. for +/// message to other threads warning that a thread has panicked (e.g., for /// monitoring purposes). /// /// # Examples @@ -806,9 +822,14 @@ const NOTIFIED: usize = 2; /// In other words, each [`Thread`] acts a bit like a spinlock that can be /// locked and unlocked using `park` and `unpark`. /// +/// Notice that being unblocked does not imply any synchronization with someone +/// that unparked this thread, it could also be spurious. +/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and +/// [`unpark`] return immediately without doing anything. +/// /// The API is typically used by acquiring a handle to the current thread, /// placing that handle in a shared data structure so that other threads can -/// find it, and then `park`ing. When some desired condition is met, another +/// find it, and then `park`ing in a loop. When some desired condition is met, another /// thread calls [`unpark`] on the handle. /// /// The motivation for this design is twofold: @@ -823,21 +844,33 @@ const NOTIFIED: usize = 2; /// /// ``` /// use std::thread; +/// use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; /// use std::time::Duration; /// -/// let parked_thread = thread::Builder::new() -/// .spawn(|| { +/// let flag = Arc::new(AtomicBool::new(false)); +/// let flag2 = Arc::clone(&flag); +/// +/// let parked_thread = thread::spawn(move || { +/// // We want to wait until the flag is set. We *could* just spin, but using +/// // park/unpark is more efficient. +/// while !flag2.load(Ordering::Acquire) { /// println!("Parking thread"); /// thread::park(); +/// // We *could* get here spuriously, i.e., way before the 10ms below are over! +/// // But that is no problem, we are in a loop until the flag is set anyway. /// println!("Thread unparked"); -/// }) -/// .unwrap(); +/// } +/// println!("Flag received"); +/// }); /// /// // Let some time pass for the thread to be spawned. /// thread::sleep(Duration::from_millis(10)); /// +/// // Set the flag, and let the thread wake up. /// // There is no race condition here, if `unpark` /// // happens first, `park` will return immediately. +/// // Hence there is no risk of a deadlock. +/// flag.store(true, Ordering::Release); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// @@ -1062,7 +1095,7 @@ struct Inner { /// Threads are represented via the `Thread` type, which you can get in one of /// two ways: /// -/// * By spawning a new thread, e.g. using the [`thread::spawn`][`spawn`] +/// * By spawning a new thread, e.g., using the [`thread::spawn`][`spawn`] /// function, and calling [`thread`][`JoinHandle::thread`] on the /// [`JoinHandle`]. /// * By requesting the current thread, using the [`thread::current`] function. diff --git a/src/libstd/time.rs b/src/libstd/time.rs index 5d0d501615f..63cede79e48 100644 --- a/src/libstd/time.rs +++ b/src/libstd/time.rs @@ -208,6 +208,22 @@ impl Instant { pub fn elapsed(&self) -> Duration { Instant::now() - *self } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[unstable(feature = "time_checked_add", issue = "55940")] + pub fn checked_add(&self, duration: Duration) -> Option<Instant> { + self.0.checked_add_duration(&duration).map(|t| Instant(t)) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[unstable(feature = "time_checked_add", issue = "55940")] + pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { + self.0.checked_sub_duration(&duration).map(|t| Instant(t)) + } } #[stable(feature = "time2", since = "1.8.0")] @@ -215,7 +231,8 @@ impl Add<Duration> for Instant { type Output = Instant; fn add(self, other: Duration) -> Instant { - Instant(self.0.add_duration(&other)) + self.checked_add(other) + .expect("overflow when adding duration to instant") } } @@ -231,7 +248,8 @@ impl Sub<Duration> for Instant { type Output = Instant; fn sub(self, other: Duration) -> Instant { - Instant(self.0.sub_duration(&other)) + self.checked_sub(other) + .expect("overflow when subtracting duration from instant") } } @@ -330,7 +348,7 @@ impl SystemTime { /// Returns the amount of time elapsed since this system time was created. /// /// This function may fail as the underlying system clock is susceptible to - /// drift and updates (e.g. the system clock could go backwards), so this + /// drift and updates (e.g., the system clock could go backwards), so this /// function may not always succeed. If successful, [`Ok`]`(`[`Duration`]`)` is /// returned where the duration represents the amount of time elapsed from /// this time measurement to the current time. @@ -365,6 +383,14 @@ impl SystemTime { pub fn checked_add(&self, duration: Duration) -> Option<SystemTime> { self.0.checked_add_duration(&duration).map(|t| SystemTime(t)) } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + #[unstable(feature = "time_checked_add", issue = "55940")] + pub fn checked_sub(&self, duration: Duration) -> Option<SystemTime> { + self.0.checked_sub_duration(&duration).map(|t| SystemTime(t)) + } } #[stable(feature = "time2", since = "1.8.0")] @@ -372,7 +398,8 @@ impl Add<Duration> for SystemTime { type Output = SystemTime; fn add(self, dur: Duration) -> SystemTime { - SystemTime(self.0.add_duration(&dur)) + self.checked_add(dur) + .expect("overflow when adding duration to instant") } } @@ -388,7 +415,8 @@ impl Sub<Duration> for SystemTime { type Output = SystemTime; fn sub(self, dur: Duration) -> SystemTime { - SystemTime(self.0.sub_duration(&dur)) + self.checked_sub(dur) + .expect("overflow when subtracting duration from instant") } } @@ -521,6 +549,20 @@ mod tests { let second = Duration::new(1, 0); assert_almost_eq!(a - second + second, a); + assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); + + // checked_add_duration will not panic on overflow + let mut maybe_t = Some(Instant::now()); + let max_duration = Duration::from_secs(u64::max_value()); + // in case `Instant` can store `>= now + max_duration`. + for _ in 0..2 { + maybe_t = maybe_t.and_then(|t| t.checked_add(max_duration)); + } + assert_eq!(maybe_t, None); + + // checked_add_duration calculates the right time and will work for another year + let year = Duration::from_secs(60 * 60 * 24 * 365); + assert_eq!(a + year, a.checked_add(year).unwrap()); } #[test] @@ -557,6 +599,7 @@ mod tests { .duration(), second); assert_almost_eq!(a - second + second, a); + assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); // A difference of 80 and 800 years cannot fit inside a 32-bit time_t if !(cfg!(unix) && ::mem::size_of::<::libc::time_t>() <= 4) { |
