about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-25 19:43:00 +0000
committerbors <bors@rust-lang.org>2023-02-25 19:43:00 +0000
commit34e6673a0473e90ef01a18eb575392c9e3859747 (patch)
tree15737c17919b7bd59f16f743150d8aac9af6096f
parent31448badfd74ea72d2c8622cc60d3dca889ef7d4 (diff)
parentaf8ee641a84d9b56c97ecc1deddcd2c7aa5fc4d4 (diff)
downloadrust-34e6673a0473e90ef01a18eb575392c9e3859747.tar.gz
rust-34e6673a0473e90ef01a18eb575392c9e3859747.zip
Auto merge of #107405 - hermitcore:bsd, r=bjorn3
add support of RustyHermit's BSD socket layer

RustyHermit is a tier 3 platform and publishes a new kernel interface. The new version supports a common BSD socket layer. By supporting this interface, the implementation of `std` can be harmonized to other operating systems. In `sys_common/mod.rs` we remove only a special case for RustyHermit. All changes are done in the RustyHermit specific directories.

To realize this socket layer, the handling of file descriptors is also harmonized to other operating systems.
-rw-r--r--Cargo.lock12
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/os/fd/owned.rs9
-rw-r--r--library/std/src/os/fd/raw.rs10
-rw-r--r--library/std/src/os/hermit/io/mod.rs13
-rw-r--r--library/std/src/os/hermit/io/net.rs46
-rw-r--r--library/std/src/os/hermit/mod.rs5
-rw-r--r--library/std/src/os/mod.rs12
-rw-r--r--library/std/src/sys/hermit/args.rs2
-rw-r--r--library/std/src/sys/hermit/fd.rs77
-rw-r--r--library/std/src/sys/hermit/fs.rs57
-rw-r--r--library/std/src/sys/hermit/mod.rs87
-rw-r--r--library/std/src/sys/hermit/net.rs639
-rw-r--r--library/std/src/sys/hermit/os.rs2
-rw-r--r--library/std/src/sys/hermit/time.rs98
-rw-r--r--library/std/src/sys_common/mod.rs1
16 files changed, 594 insertions, 478 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3ffa9e590f4..d86b8018b79 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1927,8 +1927,16 @@ version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
 dependencies = [
- "compiler_builtins",
  "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01"
+dependencies = [
+ "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
@@ -5294,7 +5302,7 @@ dependencies = [
  "dlmalloc",
  "fortanix-sgx-abi",
  "hashbrown 0.12.3",
- "hermit-abi 0.2.6",
+ "hermit-abi 0.3.0",
  "libc",
  "miniz_oxide",
  "object 0.29.0",
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 349cd91c89e..311b2e21c17 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -43,7 +43,7 @@ dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] }
 fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'] }
 
 [target.'cfg(target_os = "hermit")'.dependencies]
-hermit-abi = { version = "0.2.6", features = ['rustc-dep-of-std'] }
+hermit-abi = { version = "0.3.0", features = ['rustc-dep-of-std'] }
 
 [target.wasm32-wasi.dependencies]
 wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index 439b8d52a2d..258919d53a4 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -9,7 +9,7 @@ use crate::fs;
 use crate::io;
 use crate::marker::PhantomData;
 use crate::mem::forget;
-#[cfg(not(any(target_arch = "wasm32", target_env = "sgx")))]
+#[cfg(not(any(target_arch = "wasm32", target_env = "sgx", target_os = "hermit")))]
 use crate::sys::cvt;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 
@@ -89,7 +89,7 @@ impl OwnedFd {
 impl BorrowedFd<'_> {
     /// Creates a new `OwnedFd` instance that shares the same underlying file
     /// description as the existing `BorrowedFd` instance.
-    #[cfg(not(target_arch = "wasm32"))]
+    #[cfg(not(any(target_arch = "wasm32", target_os = "hermit")))]
     #[stable(feature = "io_safety", since = "1.63.0")]
     pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
         // We want to atomically duplicate this file descriptor and set the
@@ -112,7 +112,7 @@ impl BorrowedFd<'_> {
 
     /// Creates a new `OwnedFd` instance that shares the same underlying file
     /// description as the existing `BorrowedFd` instance.
-    #[cfg(target_arch = "wasm32")]
+    #[cfg(any(target_arch = "wasm32", target_os = "hermit"))]
     #[stable(feature = "io_safety", since = "1.63.0")]
     pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
         Err(crate::io::const_io_error!(
@@ -174,7 +174,10 @@ impl Drop for OwnedFd {
             // the file descriptor was closed or not, and if we retried (for
             // something like EINTR), we might close another valid file descriptor
             // opened after we closed ours.
+            #[cfg(not(target_os = "hermit"))]
             let _ = libc::close(self.fd);
+            #[cfg(target_os = "hermit")]
+            let _ = hermit_abi::close(self.fd);
         }
     }
 }
diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs
index c138162f1ab..0a4cefd2095 100644
--- a/library/std/src/os/fd/raw.rs
+++ b/library/std/src/os/fd/raw.rs
@@ -4,6 +4,9 @@
 
 use crate::fs;
 use crate::io;
+#[cfg(target_os = "hermit")]
+use crate::os::hermit::io::OwnedFd;
+#[cfg(not(target_os = "hermit"))]
 use crate::os::raw;
 #[cfg(all(doc, not(target_arch = "wasm32")))]
 use crate::os::unix::io::AsFd;
@@ -12,11 +15,18 @@ use crate::os::unix::io::OwnedFd;
 #[cfg(target_os = "wasi")]
 use crate::os::wasi::io::OwnedFd;
 use crate::sys_common::{AsInner, IntoInner};
+#[cfg(target_os = "hermit")]
+use hermit_abi as libc;
 
 /// Raw file descriptors.
 #[rustc_allowed_through_unstable_modules]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(not(target_os = "hermit"))]
 pub type RawFd = raw::c_int;
+#[rustc_allowed_through_unstable_modules]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg(target_os = "hermit")]
+pub type RawFd = i32;
 
 /// A trait to extract the raw file descriptor from an underlying object.
 ///
diff --git a/library/std/src/os/hermit/io/mod.rs b/library/std/src/os/hermit/io/mod.rs
new file mode 100644
index 00000000000..524dfae0d63
--- /dev/null
+++ b/library/std/src/os/hermit/io/mod.rs
@@ -0,0 +1,13 @@
+#![stable(feature = "os_fd", since = "1.66.0")]
+
+mod net;
+#[path = "../../fd/owned.rs"]
+mod owned;
+#[path = "../../fd/raw.rs"]
+mod raw;
+
+// Export the types and traits for the public API.
+#[stable(feature = "os_fd", since = "1.66.0")]
+pub use owned::*;
+#[stable(feature = "os_fd", since = "1.66.0")]
+pub use raw::*;
diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs
new file mode 100644
index 00000000000..8f3802d7873
--- /dev/null
+++ b/library/std/src/os/hermit/io/net.rs
@@ -0,0 +1,46 @@
+use crate::os::hermit::io::OwnedFd;
+use crate::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
+use crate::sys_common::{self, AsInner, FromInner, IntoInner};
+use crate::{net, sys};
+
+macro_rules! impl_as_raw_fd {
+    ($($t:ident)*) => {$(
+        #[stable(feature = "rust1", since = "1.0.0")]
+        impl AsRawFd for net::$t {
+            #[inline]
+            fn as_raw_fd(&self) -> RawFd {
+                self.as_inner().socket().as_raw_fd()
+            }
+        }
+    )*};
+}
+impl_as_raw_fd! { TcpStream TcpListener UdpSocket }
+
+macro_rules! impl_from_raw_fd {
+    ($($t:ident)*) => {$(
+        #[stable(feature = "from_raw_os", since = "1.1.0")]
+        impl FromRawFd for net::$t {
+            #[inline]
+            unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
+                unsafe {
+                    let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)));
+                    net::$t::from_inner(sys_common::net::$t::from_inner(socket))
+                }
+            }
+        }
+    )*};
+}
+impl_from_raw_fd! { TcpStream TcpListener UdpSocket }
+
+macro_rules! impl_into_raw_fd {
+    ($($t:ident)*) => {$(
+        #[stable(feature = "into_raw_os", since = "1.4.0")]
+        impl IntoRawFd for net::$t {
+            #[inline]
+            fn into_raw_fd(self) -> RawFd {
+                self.into_inner().into_socket().into_inner().into_inner().into_raw_fd()
+            }
+        }
+    )*};
+}
+impl_into_raw_fd! { TcpStream TcpListener UdpSocket }
diff --git a/library/std/src/os/hermit/mod.rs b/library/std/src/os/hermit/mod.rs
index 4657b545a1b..89b1b831912 100644
--- a/library/std/src/os/hermit/mod.rs
+++ b/library/std/src/os/hermit/mod.rs
@@ -1,6 +1,11 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
+#[allow(unused_extern_crates)]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub extern crate hermit_abi as abi;
+
 pub mod ffi;
+pub mod io;
 
 /// A prelude for conveniently writing platform-specific code.
 ///
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 42773805cdb..af137c9bd85 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -60,16 +60,6 @@ pub mod windows {}
         all(target_vendor = "fortanix", target_env = "sgx")
     )
 )))]
-#[cfg(target_os = "hermit")]
-#[path = "hermit/mod.rs"]
-pub mod unix;
-#[cfg(not(all(
-    doc,
-    any(
-        all(target_arch = "wasm32", not(target_os = "wasi")),
-        all(target_vendor = "fortanix", target_env = "sgx")
-    )
-)))]
 #[cfg(all(not(target_os = "hermit"), any(unix, doc)))]
 pub mod unix;
 
@@ -123,6 +113,8 @@ pub mod freebsd;
 pub mod fuchsia;
 #[cfg(target_os = "haiku")]
 pub mod haiku;
+#[cfg(target_os = "hermit")]
+pub mod hermit;
 #[cfg(target_os = "horizon")]
 pub mod horizon;
 #[cfg(target_os = "illumos")]
diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs
index afcae6c90ee..220a76e4b12 100644
--- a/library/std/src/sys/hermit/args.rs
+++ b/library/std/src/sys/hermit/args.rs
@@ -1,6 +1,6 @@
 use crate::ffi::{c_char, CStr, OsString};
 use crate::fmt;
-use crate::os::unix::ffi::OsStringExt;
+use crate::os::hermit::ffi::OsStringExt;
 use crate::ptr;
 use crate::sync::atomic::{
     AtomicIsize, AtomicPtr,
diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs
index c400f5f2c2e..3a2cdd301ea 100644
--- a/library/std/src/sys/hermit/fd.rs
+++ b/library/std/src/sys/hermit/fd.rs
@@ -1,36 +1,23 @@
 #![unstable(reason = "not public", issue = "none", feature = "fd")]
 
 use crate::io::{self, Read};
-use crate::mem;
+use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd};
 use crate::sys::cvt;
 use crate::sys::hermit::abi;
 use crate::sys::unsupported;
-use crate::sys_common::AsInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+use crate::os::hermit::io::*;
 
 #[derive(Debug)]
 pub struct FileDesc {
-    fd: i32,
+    fd: OwnedFd,
 }
 
 impl FileDesc {
-    pub fn new(fd: i32) -> FileDesc {
-        FileDesc { fd }
-    }
-
-    pub fn raw(&self) -> i32 {
-        self.fd
-    }
-
-    /// Extracts the actual file descriptor without closing it.
-    pub fn into_raw(self) -> i32 {
-        let fd = self.fd;
-        mem::forget(self);
-        fd
-    }
-
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
-        let result = unsafe { abi::read(self.fd, buf.as_mut_ptr(), buf.len()) };
-        cvt(result as i32)
+        let result = cvt(unsafe { abi::read(self.fd.as_raw_fd(), buf.as_mut_ptr(), buf.len()) })?;
+        Ok(result as usize)
     }
 
     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
@@ -39,8 +26,8 @@ impl FileDesc {
     }
 
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
-        let result = unsafe { abi::write(self.fd, buf.as_ptr(), buf.len()) };
-        cvt(result as i32)
+        let result = cvt(unsafe { abi::write(self.fd.as_raw_fd(), buf.as_ptr(), buf.len()) })?;
+        Ok(result as usize)
     }
 
     pub fn duplicate(&self) -> io::Result<FileDesc> {
@@ -69,19 +56,45 @@ impl<'a> Read for &'a FileDesc {
     }
 }
 
-impl AsInner<i32> for FileDesc {
-    fn as_inner(&self) -> &i32 {
+impl IntoInner<OwnedFd> for FileDesc {
+    fn into_inner(self) -> OwnedFd {
+        self.fd
+    }
+}
+
+impl FromInner<OwnedFd> for FileDesc {
+    fn from_inner(owned_fd: OwnedFd) -> Self {
+        Self { fd: owned_fd }
+    }
+}
+
+impl FromRawFd for FileDesc {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        Self { fd: FromRawFd::from_raw_fd(raw_fd) }
+    }
+}
+
+impl AsInner<OwnedFd> for FileDesc {
+    fn as_inner(&self) -> &OwnedFd {
         &self.fd
     }
 }
 
-impl Drop for FileDesc {
-    fn drop(&mut self) {
-        // Note that errors are ignored when closing a file descriptor. The
-        // reason for this is that if an error occurs we don't actually know if
-        // the file descriptor was closed or not, and if we retried (for
-        // something like EINTR), we might close another valid file descriptor
-        // (opened after we closed ours.
-        let _ = unsafe { abi::close(self.fd) };
+impl AsFd for FileDesc {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.fd.as_fd()
+    }
+}
+
+impl AsRawFd for FileDesc {
+    #[inline]
+    fn as_raw_fd(&self) -> RawFd {
+        self.fd.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for FileDesc {
+    fn into_raw_fd(self) -> RawFd {
+        self.fd.into_raw_fd()
     }
 }
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
index 6fb92c037ee..c966f217757 100644
--- a/library/std/src/sys/hermit/fs.rs
+++ b/library/std/src/sys/hermit/fs.rs
@@ -3,14 +3,17 @@ use crate::fmt;
 use crate::hash::{Hash, Hasher};
 use crate::io::{self, Error, ErrorKind};
 use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
+use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::path::{Path, PathBuf};
 use crate::sys::common::small_c_string::run_path_with_cstr;
 use crate::sys::cvt;
-use crate::sys::hermit::abi;
-use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
+use crate::sys::hermit::abi::{
+    self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY,
+};
 use crate::sys::hermit::fd::FileDesc;
 use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 
 pub use crate::sys_common::fs::{copy, try_exists};
 //pub use crate::sys_common::fs::remove_dir_all;
@@ -283,7 +286,7 @@ impl File {
         }
 
         let fd = unsafe { cvt(abi::open(path.as_ptr(), flags, mode))? };
-        Ok(File(FileDesc::new(fd as i32)))
+        Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) }))
     }
 
     pub fn file_attr(&self) -> io::Result<FileAttr> {
@@ -363,6 +366,54 @@ impl DirBuilder {
     }
 }
 
+impl AsInner<FileDesc> for File {
+    fn as_inner(&self) -> &FileDesc {
+        &self.0
+    }
+}
+
+impl AsInnerMut<FileDesc> for File {
+    fn as_inner_mut(&mut self) -> &mut FileDesc {
+        &mut self.0
+    }
+}
+
+impl IntoInner<FileDesc> for File {
+    fn into_inner(self) -> FileDesc {
+        self.0
+    }
+}
+
+impl FromInner<FileDesc> for File {
+    fn from_inner(file_desc: FileDesc) -> Self {
+        Self(file_desc)
+    }
+}
+
+impl AsFd for File {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
+}
+
+impl AsRawFd for File {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for File {
+    fn into_raw_fd(self) -> RawFd {
+        self.0.into_raw_fd()
+    }
+}
+
+impl FromRawFd for File {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        Self(FromRawFd::from_raw_fd(raw_fd))
+    }
+}
+
 pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
     unsupported()
 }
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index 20fd3dd8f09..d34a4cfedea 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -13,7 +13,7 @@
 //! compiling for wasm. That way it's a compile time error for something that's
 //! guaranteed to be a runtime error!
 
-#![allow(unsafe_op_in_unsafe_fn)]
+#![allow(missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn)]
 
 use crate::intrinsics;
 use crate::os::raw::c_char;
@@ -57,9 +57,7 @@ pub mod locks {
 }
 
 use crate::io::ErrorKind;
-
-#[allow(unused_extern_crates)]
-pub extern crate hermit_abi as abi;
+use crate::os::hermit::abi;
 
 pub fn unsupported<T>() -> crate::io::Result<T> {
     Err(unsupported_err())
@@ -126,25 +124,72 @@ pub unsafe extern "C" fn runtime_entry(
 
 pub fn decode_error_kind(errno: i32) -> ErrorKind {
     match errno {
-        x if x == 13 as i32 => ErrorKind::PermissionDenied,
-        x if x == 98 as i32 => ErrorKind::AddrInUse,
-        x if x == 99 as i32 => ErrorKind::AddrNotAvailable,
-        x if x == 11 as i32 => ErrorKind::WouldBlock,
-        x if x == 103 as i32 => ErrorKind::ConnectionAborted,
-        x if x == 111 as i32 => ErrorKind::ConnectionRefused,
-        x if x == 104 as i32 => ErrorKind::ConnectionReset,
-        x if x == 17 as i32 => ErrorKind::AlreadyExists,
-        x if x == 4 as i32 => ErrorKind::Interrupted,
-        x if x == 22 as i32 => ErrorKind::InvalidInput,
-        x if x == 2 as i32 => ErrorKind::NotFound,
-        x if x == 107 as i32 => ErrorKind::NotConnected,
-        x if x == 1 as i32 => ErrorKind::PermissionDenied,
-        x if x == 32 as i32 => ErrorKind::BrokenPipe,
-        x if x == 110 as i32 => ErrorKind::TimedOut,
+        abi::errno::EACCES => ErrorKind::PermissionDenied,
+        abi::errno::EADDRINUSE => ErrorKind::AddrInUse,
+        abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable,
+        abi::errno::EAGAIN => ErrorKind::WouldBlock,
+        abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted,
+        abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused,
+        abi::errno::ECONNRESET => ErrorKind::ConnectionReset,
+        abi::errno::EEXIST => ErrorKind::AlreadyExists,
+        abi::errno::EINTR => ErrorKind::Interrupted,
+        abi::errno::EINVAL => ErrorKind::InvalidInput,
+        abi::errno::ENOENT => ErrorKind::NotFound,
+        abi::errno::ENOTCONN => ErrorKind::NotConnected,
+        abi::errno::EPERM => ErrorKind::PermissionDenied,
+        abi::errno::EPIPE => ErrorKind::BrokenPipe,
+        abi::errno::ETIMEDOUT => ErrorKind::TimedOut,
         _ => ErrorKind::Uncategorized,
     }
 }
 
-pub fn cvt(result: i32) -> crate::io::Result<usize> {
-    if result < 0 { Err(crate::io::Error::from_raw_os_error(-result)) } else { Ok(result as usize) }
+#[doc(hidden)]
+pub trait IsNegative {
+    fn is_negative(&self) -> bool;
+    fn negate(&self) -> i32;
+}
+
+macro_rules! impl_is_negative {
+    ($($t:ident)*) => ($(impl IsNegative for $t {
+        fn is_negative(&self) -> bool {
+            *self < 0
+        }
+
+        fn negate(&self) -> i32 {
+            i32::try_from(-(*self)).unwrap()
+        }
+    })*)
+}
+
+impl IsNegative for i32 {
+    fn is_negative(&self) -> bool {
+        *self < 0
+    }
+
+    fn negate(&self) -> i32 {
+        -(*self)
+    }
+}
+impl_is_negative! { i8 i16 i64 isize }
+
+pub fn cvt<T: IsNegative>(t: T) -> crate::io::Result<T> {
+    if t.is_negative() {
+        let e = decode_error_kind(t.negate());
+        Err(crate::io::Error::from(e))
+    } else {
+        Ok(t)
+    }
+}
+
+pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
+where
+    T: IsNegative,
+    F: FnMut() -> T,
+{
+    loop {
+        match cvt(f()) {
+            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+            other => return other,
+        }
+    }
 }
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
index 8a13879d8cc..5fb6281aa1e 100644
--- a/library/std/src/sys/hermit/net.rs
+++ b/library/std/src/sys/hermit/net.rs
@@ -1,490 +1,353 @@
-use crate::fmt;
-use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
-use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
-use crate::str;
-use crate::sync::Arc;
-use crate::sys::hermit::abi;
-use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
-use crate::sys::unsupported;
-use crate::sys_common::AsInner;
+#![allow(dead_code)]
+
+use crate::cmp;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem;
+use crate::net::{Shutdown, SocketAddr};
+use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
+use crate::sys::hermit::fd::FileDesc;
+use crate::sys::time::Instant;
+use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::time::Duration;
 
-/// Checks whether the HermitCore's socket interface has been started already, and
-/// if not, starts it.
-pub fn init() -> io::Result<()> {
-    if abi::network_init() < 0 {
-        return Err(io::const_io_error!(
-            ErrorKind::Uncategorized,
-            "Unable to initialize network interface",
-        ));
-    }
-
-    Ok(())
-}
-
-#[derive(Debug, Clone)]
-pub struct Socket(abi::Handle);
-
-impl AsInner<abi::Handle> for Socket {
-    fn as_inner(&self) -> &abi::Handle {
-        &self.0
-    }
-}
+use core::ffi::c_int;
 
-impl Drop for Socket {
-    fn drop(&mut self) {
-        let _ = abi::tcpstream::close(self.0);
-    }
-}
+#[allow(unused_extern_crates)]
+pub extern crate hermit_abi as netc;
 
-// Arc is used to count the number of used sockets.
-// Only if all sockets are released, the drop
-// method will close the socket.
-#[derive(Clone)]
-pub struct TcpStream(Arc<Socket>);
-
-impl TcpStream {
-    pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
-        let addr = addr?;
-
-        match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
-            Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
-            _ => Err(io::const_io_error!(
-                ErrorKind::Uncategorized,
-                "Unable to initiate a connection on a socket",
-            )),
-        }
-    }
+pub use crate::sys::{cvt, cvt_r};
 
-    pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
-        match abi::tcpstream::connect(
-            saddr.ip().to_string().as_bytes(),
-            saddr.port(),
-            Some(duration.as_millis() as u64),
-        ) {
-            Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
-            _ => Err(io::const_io_error!(
-                ErrorKind::Uncategorized,
-                "Unable to initiate a connection on a socket",
-            )),
-        }
-    }
+pub type wrlen_t = usize;
 
-    pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
-        abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
-            .map_err(|_| {
-                io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value")
-            })
+pub fn cvt_gai(err: i32) -> io::Result<()> {
+    if err == 0 {
+        return Ok(());
     }
 
-    pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
-        abi::tcpstream::set_write_timeout(
-            *self.0.as_inner(),
-            duration.map(|d| d.as_millis() as u64),
-        )
-        .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value"))
-    }
+    let detail = "";
 
-    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
-            io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
-        })?;
+    Err(io::Error::new(
+        io::ErrorKind::Uncategorized,
+        &format!("failed to lookup address information: {detail}")[..],
+    ))
+}
 
-        Ok(duration.map(|d| Duration::from_millis(d)))
+/// Checks whether the HermitCore's socket interface has been started already, and
+/// if not, starts it.
+pub fn init() {
+    if unsafe { netc::network_init() } < 0 {
+        panic!("Unable to initialize network interface");
     }
+}
 
-    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
-            io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
-        })?;
+#[derive(Debug)]
+pub struct Socket(FileDesc);
 
-        Ok(duration.map(|d| Duration::from_millis(d)))
+impl Socket {
+    pub fn new(addr: &SocketAddr, ty: i32) -> io::Result<Socket> {
+        let fam = match *addr {
+            SocketAddr::V4(..) => netc::AF_INET,
+            SocketAddr::V6(..) => netc::AF_INET6,
+        };
+        Socket::new_raw(fam, ty)
     }
 
-    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
-        abi::tcpstream::peek(*self.0.as_inner(), buf)
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peek failed"))
+    pub fn new_raw(fam: i32, ty: i32) -> io::Result<Socket> {
+        let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?;
+        Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) }))
     }
 
-    pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
-        self.read_vectored(&mut [IoSliceMut::new(buffer)])
+    pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> {
+        unimplemented!()
     }
 
-    pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        let mut size: usize = 0;
-
-        for i in ioslice.iter_mut() {
-            let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
-                io::const_io_error!(ErrorKind::Uncategorized, "Unable to read on socket")
-            })?;
+    pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
+        self.set_nonblocking(true)?;
+        let r = unsafe {
+            let (addr, len) = addr.into_inner();
+            cvt(netc::connect(self.as_raw_fd(), addr.as_ptr(), len))
+        };
+        self.set_nonblocking(false)?;
 
-            if ret != 0 {
-                size += ret;
-            }
+        match r {
+            Ok(_) => return Ok(()),
+            // there's no ErrorKind for EINPROGRESS :(
+            Err(ref e) if e.raw_os_error() == Some(netc::errno::EINPROGRESS) => {}
+            Err(e) => return Err(e),
         }
 
-        Ok(size)
-    }
-
-    #[inline]
-    pub fn is_read_vectored(&self) -> bool {
-        true
-    }
-
-    pub fn write(&self, buffer: &[u8]) -> io::Result<usize> {
-        self.write_vectored(&[IoSlice::new(buffer)])
-    }
+        let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 };
 
-    pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result<usize> {
-        let mut size: usize = 0;
-
-        for i in ioslice.iter() {
-            size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
-                io::const_io_error!(ErrorKind::Uncategorized, "Unable to write on socket")
-            })?;
+        if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
+            return Err(io::const_io_error!(
+                io::ErrorKind::InvalidInput,
+                "cannot set a 0 duration timeout",
+            ));
         }
 
-        Ok(size)
-    }
-
-    #[inline]
-    pub fn is_write_vectored(&self) -> bool {
-        true
-    }
-
-    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"))?;
+        let start = Instant::now();
 
-        let saddr = match ipaddr {
-            Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
-            Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
-            _ => {
-                return Err(io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"));
+        loop {
+            let elapsed = start.elapsed();
+            if elapsed >= timeout {
+                return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out"));
             }
-        };
-
-        Ok(saddr)
-    }
-
-    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        unsupported()
-    }
-
-    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
-        abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to shutdown socket"))
-    }
-
-    pub fn duplicate(&self) -> io::Result<TcpStream> {
-        Ok(self.clone())
-    }
-
-    pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> {
-        unsupported()
-    }
-
-    pub fn linger(&self) -> io::Result<Option<Duration>> {
-        unsupported()
-    }
-
-    pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
-        abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "set_nodelay failed"))
-    }
-
-    pub fn nodelay(&self) -> io::Result<bool> {
-        abi::tcpstream::nodelay(*self.0.as_inner())
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "nodelay failed"))
-    }
-
-    pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
-        abi::tcpstream::set_tll(*self.0.as_inner(), tll)
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to set TTL"))
-    }
-
-    pub fn ttl(&self) -> io::Result<u32> {
-        abi::tcpstream::get_tll(*self.0.as_inner())
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to get TTL"))
-    }
 
-    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        unsupported()
-    }
-
-    pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
-        abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| {
-            io::const_io_error!(ErrorKind::Uncategorized, "unable to set blocking mode")
-        })
-    }
-}
-
-impl fmt::Debug for TcpStream {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        Ok(())
-    }
-}
-
-#[derive(Clone)]
-pub struct TcpListener(SocketAddr);
-
-impl TcpListener {
-    pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
-        let addr = addr?;
-
-        Ok(TcpListener(*addr))
-    }
-
-    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        Ok(self.0)
-    }
-
-    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
-        let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
-            .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "accept failed"))?;
-        let saddr = match ipaddr {
-            Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
-            Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
-            _ => {
-                return Err(io::const_io_error!(ErrorKind::Uncategorized, "accept failed"));
+            let timeout = timeout - elapsed;
+            let mut timeout = timeout
+                .as_secs()
+                .saturating_mul(1_000)
+                .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000);
+            if timeout == 0 {
+                timeout = 1;
             }
-        };
-
-        Ok((TcpStream(Arc::new(Socket(handle))), saddr))
-    }
 
-    pub fn duplicate(&self) -> io::Result<TcpListener> {
-        Ok(self.clone())
-    }
-
-    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        unsupported()
-    }
-
-    pub fn ttl(&self) -> io::Result<u32> {
-        unsupported()
-    }
-
-    pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
-        unsupported()
+            let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int;
+
+            match unsafe { netc::poll(&mut pollfd, 1, timeout) } {
+                -1 => {
+                    let err = io::Error::last_os_error();
+                    if err.kind() != io::ErrorKind::Interrupted {
+                        return Err(err);
+                    }
+                }
+                0 => {}
+                _ => {
+                    // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look
+                    // for POLLHUP rather than read readiness
+                    if pollfd.revents & netc::POLLHUP != 0 {
+                        let e = self.take_error()?.unwrap_or_else(|| {
+                            io::const_io_error!(
+                                io::ErrorKind::Uncategorized,
+                                "no error set after POLLHUP",
+                            )
+                        });
+                        return Err(e);
+                    }
+
+                    return Ok(());
+                }
+            }
+        }
     }
 
-    pub fn only_v6(&self) -> io::Result<bool> {
-        unsupported()
+    pub fn accept(
+        &self,
+        storage: *mut netc::sockaddr,
+        len: *mut netc::socklen_t,
+    ) -> io::Result<Socket> {
+        let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?;
+        Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) }))
     }
 
-    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        unsupported()
+    pub fn duplicate(&self) -> io::Result<Socket> {
+        let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?;
+        Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) }))
     }
 
-    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        unsupported()
+    fn recv_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<usize> {
+        let ret =
+            cvt(unsafe { netc::recv(self.0.as_raw_fd(), buf.as_mut_ptr(), buf.len(), flags) })?;
+        Ok(ret as usize)
     }
-}
 
-impl fmt::Debug for TcpListener {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        Ok(())
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.recv_with_flags(buf, 0)
     }
-}
-
-pub struct UdpSocket(abi::Handle);
 
-impl UdpSocket {
-    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
-        unsupported()
+    pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.recv_with_flags(buf, netc::MSG_PEEK)
     }
 
-    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
-        unsupported()
-    }
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        let mut size: isize = 0;
 
-    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
-        unsupported()
-    }
+        for i in bufs.iter_mut() {
+            let ret: isize =
+                cvt(unsafe { netc::read(self.0.as_raw_fd(), i.as_mut_ptr(), i.len()) })?;
 
-    pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        unsupported()
-    }
+            if ret != 0 {
+                size += ret;
+            }
+        }
 
-    pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
-        unsupported()
+        Ok(size.try_into().unwrap())
     }
 
-    pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
-        unsupported()
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
     }
 
-    pub fn duplicate(&self) -> io::Result<UdpSocket> {
-        unsupported()
-    }
+    fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> {
+        let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() };
+        let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t;
 
-    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        unsupported()
+        let n = cvt(unsafe {
+            netc::recvfrom(
+                self.as_raw_fd(),
+                buf.as_mut_ptr(),
+                buf.len(),
+                flags,
+                &mut storage as *mut _ as *mut _,
+                &mut addrlen,
+            )
+        })?;
+        Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?))
     }
 
-    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
-        unsupported()
+    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_with_flags(buf, 0)
     }
 
-    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
-        unsupported()
+    pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.recv_from_with_flags(buf, netc::MSG_PEEK)
     }
 
-    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
-        unsupported()
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?;
+        Ok(sz.try_into().unwrap())
     }
 
-    pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
-        unsupported()
-    }
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        let mut size: isize = 0;
 
-    pub fn broadcast(&self) -> io::Result<bool> {
-        unsupported()
-    }
+        for i in bufs.iter() {
+            size += cvt(unsafe { netc::write(self.0.as_raw_fd(), i.as_ptr(), i.len()) })?;
+        }
 
-    pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
-        unsupported()
+        Ok(size.try_into().unwrap())
     }
 
-    pub fn multicast_loop_v4(&self) -> io::Result<bool> {
-        unsupported()
+    pub fn is_write_vectored(&self) -> bool {
+        true
     }
 
-    pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
-        unsupported()
-    }
+    pub fn set_timeout(&self, dur: Option<Duration>, kind: i32) -> io::Result<()> {
+        let timeout = match dur {
+            Some(dur) => {
+                if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
+                    return Err(io::const_io_error!(
+                        io::ErrorKind::InvalidInput,
+                        "cannot set a 0 duration timeout",
+                    ));
+                }
+
+                let secs = if dur.as_secs() > netc::time_t::MAX as u64 {
+                    netc::time_t::MAX
+                } else {
+                    dur.as_secs() as netc::time_t
+                };
+                let mut timeout = netc::timeval {
+                    tv_sec: secs,
+                    tv_usec: dur.subsec_micros() as netc::suseconds_t,
+                };
+                if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
+                    timeout.tv_usec = 1;
+                }
+                timeout
+            }
+            None => netc::timeval { tv_sec: 0, tv_usec: 0 },
+        };
 
-    pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
-        unsupported()
+        setsockopt(self, netc::SOL_SOCKET, kind, timeout)
     }
 
-    pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
-        unsupported()
+    pub fn timeout(&self, kind: i32) -> io::Result<Option<Duration>> {
+        let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?;
+        if raw.tv_sec == 0 && raw.tv_usec == 0 {
+            Ok(None)
+        } else {
+            let sec = raw.tv_sec as u64;
+            let nsec = (raw.tv_usec as u32) * 1000;
+            Ok(Some(Duration::new(sec, nsec)))
+        }
     }
 
-    pub fn multicast_loop_v6(&self) -> io::Result<bool> {
-        unsupported()
+    pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+        let how = match how {
+            Shutdown::Write => netc::SHUT_WR,
+            Shutdown::Read => netc::SHUT_RD,
+            Shutdown::Both => netc::SHUT_RDWR,
+        };
+        cvt(unsafe { netc::shutdown_socket(self.as_raw_fd(), how) })?;
+        Ok(())
     }
 
-    pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        unsupported()
-    }
+    pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
+        let linger = netc::linger {
+            l_onoff: linger.is_some() as i32,
+            l_linger: linger.unwrap_or_default().as_secs() as libc::c_int,
+        };
 
-    pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        unsupported()
+        setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger)
     }
 
-    pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
-        unsupported()
-    }
+    pub fn linger(&self) -> io::Result<Option<Duration>> {
+        let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?;
 
-    pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
-        unsupported()
+        Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64)))
     }
 
-    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
-        unsupported()
+    pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
+        let value: i32 = if nodelay { 1 } else { 0 };
+        setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value)
     }
 
-    pub fn ttl(&self) -> io::Result<u32> {
-        unsupported()
+    pub fn nodelay(&self) -> io::Result<bool> {
+        let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?;
+        Ok(raw != 0)
+    }
+
+    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+        let mut nonblocking: i32 = if nonblocking { 1 } else { 0 };
+        cvt(unsafe {
+            netc::ioctl(
+                self.as_raw_fd(),
+                netc::FIONBIO,
+                &mut nonblocking as *mut _ as *mut core::ffi::c_void,
+            )
+        })
+        .map(drop)
     }
 
     pub fn take_error(&self) -> io::Result<Option<io::Error>> {
-        unsupported()
-    }
-
-    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
-        unsupported()
-    }
-
-    pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
-        unsupported()
-    }
-
-    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
-        unsupported()
+        unimplemented!()
     }
 
-    pub fn send(&self, _: &[u8]) -> io::Result<usize> {
-        unsupported()
-    }
-
-    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
-        unsupported()
+    // This is used by sys_common code to abstract over Windows and Unix.
+    pub fn as_raw(&self) -> RawFd {
+        self.0.as_raw_fd()
     }
 }
 
-impl fmt::Debug for UdpSocket {
-    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        Ok(())
+impl AsInner<FileDesc> for Socket {
+    fn as_inner(&self) -> &FileDesc {
+        &self.0
     }
 }
 
-pub struct LookupHost(!);
-
-impl LookupHost {
-    pub fn port(&self) -> u16 {
+impl IntoInner<FileDesc> for Socket {
+    fn into_inner(self) -> FileDesc {
         self.0
     }
 }
 
-impl Iterator for LookupHost {
-    type Item = SocketAddr;
-    fn next(&mut self) -> Option<SocketAddr> {
-        self.0
+impl FromInner<FileDesc> for Socket {
+    fn from_inner(file_desc: FileDesc) -> Self {
+        Self(file_desc)
     }
 }
 
-impl TryFrom<&str> for LookupHost {
-    type Error = io::Error;
-
-    fn try_from(_v: &str) -> io::Result<LookupHost> {
-        unsupported()
+impl AsFd for Socket {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
     }
 }
 
-impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
-    type Error = io::Error;
-
-    fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
-        unsupported()
+impl AsRawFd for Socket {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
     }
 }
-
-#[allow(nonstandard_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 {}
-}
diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs
index 8f927df85be..e53dbae6119 100644
--- a/library/std/src/sys/hermit/os.rs
+++ b/library/std/src/sys/hermit/os.rs
@@ -4,7 +4,7 @@ use crate::ffi::{CStr, OsStr, OsString};
 use crate::fmt;
 use crate::io;
 use crate::marker::PhantomData;
-use crate::os::unix::ffi::OsStringExt;
+use crate::os::hermit::ffi::OsStringExt;
 use crate::path::{self, PathBuf};
 use crate::str;
 use crate::sync::Mutex;
diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs
index c17e6c8af62..32ddc4346ee 100644
--- a/library/std/src/sys/hermit/time.rs
+++ b/library/std/src/sys/hermit/time.rs
@@ -1,6 +1,7 @@
 #![allow(dead_code)]
 
 use crate::cmp::Ordering;
+use crate::ops::{Add, AddAssign, Sub, SubAssign};
 use crate::sys::hermit::abi;
 use crate::sys::hermit::abi::timespec;
 use crate::sys::hermit::abi::{CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC};
@@ -102,55 +103,122 @@ impl Hash for Timespec {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
-pub struct Instant {
-    t: Timespec,
-}
+pub struct Instant(Timespec);
 
 impl Instant {
     pub fn now() -> Instant {
         let mut time: Timespec = Timespec::zero();
         let _ = unsafe { abi::clock_gettime(CLOCK_MONOTONIC, &mut time.t as *mut timespec) };
 
-        Instant { t: time }
+        Instant(time)
+    }
+
+    #[stable(feature = "time2", since = "1.8.0")]
+    pub fn elapsed(&self) -> Duration {
+        Instant::now() - *self
+    }
+
+    pub fn duration_since(&self, earlier: Instant) -> Duration {
+        self.checked_duration_since(earlier).unwrap_or_default()
+    }
+
+    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
+        self.checked_sub_instant(&earlier)
     }
 
     pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
-        self.t.sub_timespec(&other.t).ok()
+        self.0.sub_timespec(&other.0).ok()
     }
 
     pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
-        Some(Instant { t: self.t.checked_add_duration(other)? })
+        Some(Instant(self.0.checked_add_duration(other)?))
     }
 
     pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
-        Some(Instant { t: self.t.checked_sub_duration(other)? })
+        Some(Instant(self.0.checked_sub_duration(other)?))
+    }
+
+    pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
+        self.0.checked_add_duration(&duration).map(Instant)
+    }
+
+    pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
+        self.0.checked_sub_duration(&duration).map(Instant)
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
-pub struct SystemTime {
-    t: Timespec,
+impl Add<Duration> for Instant {
+    type Output = Instant;
+
+    /// # Panics
+    ///
+    /// This function may panic if the resulting point in time cannot be represented by the
+    /// underlying data structure. See [`Instant::checked_add`] for a version without panic.
+    fn add(self, other: Duration) -> Instant {
+        self.checked_add(other).expect("overflow when adding duration to instant")
+    }
 }
 
-pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
+impl AddAssign<Duration> for Instant {
+    fn add_assign(&mut self, other: Duration) {
+        *self = *self + other;
+    }
+}
+
+impl Sub<Duration> for Instant {
+    type Output = Instant;
+
+    fn sub(self, other: Duration) -> Instant {
+        self.checked_sub(other).expect("overflow when subtracting duration from instant")
+    }
+}
+
+impl SubAssign<Duration> for Instant {
+    fn sub_assign(&mut self, other: Duration) {
+        *self = *self - other;
+    }
+}
+
+impl Sub<Instant> for Instant {
+    type Output = Duration;
+
+    /// Returns the amount of time elapsed from another instant to this one,
+    /// or zero duration if that instant is later than this one.
+    ///
+    /// # Panics
+    ///
+    /// Previous rust versions panicked when `other` was later than `self`. Currently this
+    /// method saturates. Future versions may reintroduce the panic in some circumstances.
+    /// See [Monotonicity].
+    ///
+    /// [Monotonicity]: Instant#monotonicity
+    fn sub(self, other: Instant) -> Duration {
+        self.duration_since(other)
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub struct SystemTime(Timespec);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
 
 impl SystemTime {
     pub fn now() -> SystemTime {
         let mut time: Timespec = Timespec::zero();
         let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, &mut time.t as *mut timespec) };
 
-        SystemTime { t: time }
+        SystemTime(time)
     }
 
     pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
-        self.t.sub_timespec(&other.t)
+        self.0.sub_timespec(&other.0)
     }
 
     pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
-        Some(SystemTime { t: self.t.checked_add_duration(other)? })
+        Some(SystemTime(self.0.checked_add_duration(other)?))
     }
 
     pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
-        Some(SystemTime { t: self.t.checked_sub_duration(other)? })
+        Some(SystemTime(self.0.checked_sub_duration(other)?))
     }
 }
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index 6b24b0e9aa8..e9c727cbbd1 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -44,7 +44,6 @@ cfg_if::cfg_if! {
 
 cfg_if::cfg_if! {
     if #[cfg(any(target_os = "l4re",
-                 target_os = "hermit",
                  feature = "restricted-std",
                  all(target_family = "wasm", not(target_os = "emscripten")),
                  all(target_vendor = "fortanix", target_env = "sgx")))] {