about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Bartels <Florian.Bartels@elektrobit.com>2023-01-10 10:44:05 +0100
committerFlorian Bartels <Florian.Bartels@elektrobit.com>2023-02-28 15:59:47 +0100
commit3ce2cd059f8f7c69d9e1fe26b95cec2bfd3c98a7 (patch)
treed5944496239e8b36be6d55fac7c285a6a12a3e48
parent8f41570e91fcbad2f55800f2edcdea67c3389dc7 (diff)
downloadrust-3ce2cd059f8f7c69d9e1fe26b95cec2bfd3c98a7.tar.gz
rust-3ce2cd059f8f7c69d9e1fe26b95cec2bfd3c98a7.zip
Add QNX Neutrino support to libstd
Co-authored-by: gh-tr <troach@qnx.com>
-rw-r--r--library/core/src/ffi/mod.rs1
-rw-r--r--library/std/build.rs1
-rw-r--r--library/std/src/net/tcp/tests.rs5
-rw-r--r--library/std/src/net/udp/tests.rs5
-rw-r--r--library/std/src/os/mod.rs2
-rw-r--r--library/std/src/os/nto/fs.rs92
-rw-r--r--library/std/src/os/nto/mod.rs4
-rw-r--r--library/std/src/os/nto/raw.rs40
-rw-r--r--library/std/src/os/unix/mod.rs5
-rw-r--r--library/std/src/os/unix/net/datagram.rs6
-rw-r--r--library/std/src/os/unix/net/tests.rs1
-rw-r--r--library/std/src/os/unix/process.rs26
-rw-r--r--library/std/src/os/unix/ucred.rs3
-rw-r--r--library/std/src/sys/unix/args.rs3
-rw-r--r--library/std/src/sys/unix/env.rs11
-rw-r--r--library/std/src/sys/unix/fd.rs14
-rw-r--r--library/std/src/sys/unix/fs.rs84
-rw-r--r--library/std/src/sys/unix/locks/pthread_condvar.rs11
-rw-r--r--library/std/src/sys/unix/net.rs2
-rw-r--r--library/std/src/sys/unix/os.rs12
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs10
-rw-r--r--library/std/src/sys/unix/thread.rs15
-rw-r--r--library/std/src/sys/unix/thread_parking/pthread.rs9
-rw-r--r--library/std/src/sys/unix/time.rs22
-rw-r--r--library/std/src/sys_common/backtrace.rs13
-rw-r--r--library/std/src/sys_common/net.rs7
-rw-r--r--library/std/src/sys_common/thread_local_key.rs10
-rw-r--r--library/unwind/src/lib.rs4
-rw-r--r--src/doc/rustc/src/platform-support/nto-qnx.md175
-rw-r--r--tests/codegen/thread-local.rs1
-rw-r--r--tests/ui/abi/stack-probes-lto.rs1
-rw-r--r--tests/ui/abi/stack-probes.rs1
-rw-r--r--tests/ui/command/command-setgroups.rs1
-rw-r--r--tests/ui/intrinsics/intrinsic-alignment.rs4
-rw-r--r--tests/ui/process/process-sigpipe.rs1
-rw-r--r--tests/ui/runtime/out-of-stack.rs1
-rw-r--r--tests/ui/runtime/signal-alternate-stack-cleanup.rs1
-rw-r--r--tests/ui/structs-enums/rec-align-u64.rs4
-rw-r--r--tests/ui/thread-local/tls.rs1
-rw-r--r--tests/ui/wait-forked-but-failed-child.rs1
40 files changed, 535 insertions, 75 deletions
diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs
index 76daceecd7b..27f6659049c 100644
--- a/library/core/src/ffi/mod.rs
+++ b/library/core/src/ffi/mod.rs
@@ -144,6 +144,7 @@ mod c_char_definition {
                 )
             ),
             all(target_os = "fuchsia", target_arch = "aarch64"),
+            all(target_os = "nto", target_arch = "aarch64"),
             target_os = "horizon"
         ))] {
             pub type c_char = u8;
diff --git a/library/std/build.rs b/library/std/build.rs
index 8b1a06ee750..ea879667558 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -31,6 +31,7 @@ fn main() {
         || target.contains("espidf")
         || target.contains("solid")
         || target.contains("nintendo-3ds")
+        || target.contains("nto")
     {
         // These platforms don't have any special requirements.
     } else {
diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs
index 8c0adcfb0eb..e019bc0b67a 100644
--- a/library/std/src/net/tcp/tests.rs
+++ b/library/std/src/net/tcp/tests.rs
@@ -670,7 +670,10 @@ fn debug() {
 // FIXME: re-enabled openbsd tests once their socket timeout code
 //        no longer has rounding errors.
 // VxWorks ignores SO_SNDTIMEO.
-#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)]
+#[cfg_attr(
+    any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"),
+    ignore
+)]
 #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
 #[test]
 fn timeouts() {
diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs
index f82904ffbbf..892fe2ba8ba 100644
--- a/library/std/src/net/udp/tests.rs
+++ b/library/std/src/net/udp/tests.rs
@@ -180,7 +180,10 @@ fn debug() {
 // FIXME: re-enabled openbsd/netbsd tests once their socket timeout code
 //        no longer has rounding errors.
 // VxWorks ignores SO_SNDTIMEO.
-#[cfg_attr(any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks"), ignore)]
+#[cfg_attr(
+    any(target_os = "netbsd", target_os = "openbsd", target_os = "vxworks", target_os = "nto"),
+    ignore
+)]
 #[test]
 fn timeouts() {
     let addr = next_test_ip4();
diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs
index 42773805cdb..8bf78b9870a 100644
--- a/library/std/src/os/mod.rs
+++ b/library/std/src/os/mod.rs
@@ -135,6 +135,8 @@ pub mod l4re;
 pub mod macos;
 #[cfg(target_os = "netbsd")]
 pub mod netbsd;
+#[cfg(target_os = "nto")]
+pub mod nto;
 #[cfg(target_os = "openbsd")]
 pub mod openbsd;
 #[cfg(target_os = "redox")]
diff --git a/library/std/src/os/nto/fs.rs b/library/std/src/os/nto/fs.rs
new file mode 100644
index 00000000000..8f915b08c9e
--- /dev/null
+++ b/library/std/src/os/nto/fs.rs
@@ -0,0 +1,92 @@
+#![stable(feature = "metadata_ext", since = "1.1.0")]
+
+use crate::fs::Metadata;
+use crate::sys_common::AsInner;
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+pub trait MetadataExt {
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_dev(&self) -> u64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_ino(&self) -> u64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_mode(&self) -> u32;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_nlink(&self) -> u64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_uid(&self) -> u32;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_gid(&self) -> u32;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_rdev(&self) -> u64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_size(&self) -> u64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_atime(&self) -> i64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_atime_nsec(&self) -> i64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_mtime(&self) -> i64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_mtime_nsec(&self) -> i64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_ctime(&self) -> i64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_ctime_nsec(&self) -> i64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_blksize(&self) -> u64;
+    #[stable(feature = "metadata_ext2", since = "1.8.0")]
+    fn st_blocks(&self) -> u64;
+}
+
+#[stable(feature = "metadata_ext", since = "1.1.0")]
+impl MetadataExt for Metadata {
+    fn st_dev(&self) -> u64 {
+        self.as_inner().as_inner().st_dev as u64
+    }
+    fn st_ino(&self) -> u64 {
+        self.as_inner().as_inner().st_ino as u64
+    }
+    fn st_mode(&self) -> u32 {
+        self.as_inner().as_inner().st_mode as u32
+    }
+    fn st_nlink(&self) -> u64 {
+        self.as_inner().as_inner().st_nlink as u64
+    }
+    fn st_uid(&self) -> u32 {
+        self.as_inner().as_inner().st_uid as u32
+    }
+    fn st_gid(&self) -> u32 {
+        self.as_inner().as_inner().st_gid as u32
+    }
+    fn st_rdev(&self) -> u64 {
+        self.as_inner().as_inner().st_rdev as u64
+    }
+    fn st_size(&self) -> u64 {
+        self.as_inner().as_inner().st_size as u64
+    }
+    fn st_atime(&self) -> i64 {
+        self.as_inner().as_inner().st_atim.tv_sec as i64
+    }
+    fn st_atime_nsec(&self) -> i64 {
+        self.as_inner().as_inner().st_atim.tv_nsec as i64
+    }
+    fn st_mtime(&self) -> i64 {
+        self.as_inner().as_inner().st_mtim.tv_sec as i64
+    }
+    fn st_mtime_nsec(&self) -> i64 {
+        self.as_inner().as_inner().st_mtim.tv_nsec as i64
+    }
+    fn st_ctime(&self) -> i64 {
+        self.as_inner().as_inner().st_ctim.tv_sec as i64
+    }
+    fn st_ctime_nsec(&self) -> i64 {
+        self.as_inner().as_inner().st_ctim.tv_nsec as i64
+    }
+    fn st_blksize(&self) -> u64 {
+        self.as_inner().as_inner().st_blksize as u64
+    }
+    fn st_blocks(&self) -> u64 {
+        self.as_inner().as_inner().st_blocks as u64
+    }
+}
diff --git a/library/std/src/os/nto/mod.rs b/library/std/src/os/nto/mod.rs
new file mode 100644
index 00000000000..e02ba8f1bb5
--- /dev/null
+++ b/library/std/src/os/nto/mod.rs
@@ -0,0 +1,4 @@
+#![stable(feature = "raw_ext", since = "1.1.0")]
+
+pub mod fs;
+pub mod raw;
diff --git a/library/std/src/os/nto/raw.rs b/library/std/src/os/nto/raw.rs
new file mode 100644
index 00000000000..90e9ad54643
--- /dev/null
+++ b/library/std/src/os/nto/raw.rs
@@ -0,0 +1,40 @@
+#![stable(feature = "raw_ext", since = "1.1.0")]
+#![deprecated(
+    since = "1.8.0",
+    note = "these type aliases are no longer supported by \
+            the standard library, the `libc` crate on \
+            crates.io should be used instead for the correct \
+            definitions"
+)]
+#![allow(deprecated)]
+
+use crate::os::raw::c_int;
+
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type dev_t = u32;
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub type mode_t = u32;
+
+#[stable(feature = "pthread_t", since = "1.8.0")]
+pub type pthread_t = c_int;
+
+#[doc(inline)]
+#[stable(feature = "raw_ext", since = "1.1.0")]
+pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, time_t};
+
+mod arch {
+    use crate::os::raw::c_long;
+
+    #[stable(feature = "raw_ext", since = "1.1.0")]
+    pub type blkcnt_t = i64;
+    #[stable(feature = "raw_ext", since = "1.1.0")]
+    pub type blksize_t = i32;
+    #[stable(feature = "raw_ext", since = "1.1.0")]
+    pub type ino_t = u64;
+    #[stable(feature = "raw_ext", since = "1.1.0")]
+    pub type nlink_t = u32;
+    #[stable(feature = "raw_ext", since = "1.1.0")]
+    pub type off_t = i64;
+    #[stable(feature = "raw_ext", since = "1.1.0")]
+    pub type time_t = c_long;
+}
diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs
index f97fa0fb06f..eb2d7ce1174 100644
--- a/library/std/src/os/unix/mod.rs
+++ b/library/std/src/os/unix/mod.rs
@@ -65,6 +65,8 @@ mod platform {
     pub use crate::os::macos::*;
     #[cfg(target_os = "netbsd")]
     pub use crate::os::netbsd::*;
+    #[cfg(target_os = "nto")]
+    pub use crate::os::nto::*;
     #[cfg(target_os = "openbsd")]
     pub use crate::os::openbsd::*;
     #[cfg(target_os = "redox")]
@@ -95,7 +97,8 @@ pub mod thread;
     target_os = "watchos",
     target_os = "macos",
     target_os = "netbsd",
-    target_os = "openbsd"
+    target_os = "openbsd",
+    target_os = "nto",
 ))]
 pub mod ucred;
 
diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs
index f758f88d0a3..272b4f5dcd5 100644
--- a/library/std/src/os/unix/net/datagram.rs
+++ b/library/std/src/os/unix/net/datagram.rs
@@ -19,7 +19,8 @@ use crate::{fmt, io};
     target_os = "freebsd",
     target_os = "openbsd",
     target_os = "netbsd",
-    target_os = "haiku"
+    target_os = "haiku",
+    target_os = "nto",
 ))]
 use libc::MSG_NOSIGNAL;
 #[cfg(not(any(
@@ -29,7 +30,8 @@ use libc::MSG_NOSIGNAL;
     target_os = "freebsd",
     target_os = "openbsd",
     target_os = "netbsd",
-    target_os = "haiku"
+    target_os = "haiku",
+    target_os = "nto",
 )))]
 const MSG_NOSIGNAL: libc::c_int = 0x0;
 
diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs
index 37fcfa8446b..f8c29a6d3a1 100644
--- a/library/std/src/os/unix/net/tests.rs
+++ b/library/std/src/os/unix/net/tests.rs
@@ -167,6 +167,7 @@ fn long_path() {
 }
 
 #[test]
+#[cfg(not(target_os = "nto"))]
 fn timeouts() {
     let dir = tmpdir();
     let socket_path = dir.path().join("sock");
diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 09b2bfe39f0..729c63d184f 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -12,15 +12,23 @@ use crate::sealed::Sealed;
 use crate::sys;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 
-#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
-type UserId = u32;
-#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
-type GroupId = u32;
-
-#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))]
-type UserId = u16;
-#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))]
-type GroupId = u16;
+use cfg_if::cfg_if;
+
+cfg_if! {
+    if #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))] {
+        type UserId = u16;
+        type GroupId = u16;
+    } else if #[cfg(target_os = "nto")] {
+        // Both IDs are signed, see `sys/target_nto.h` of the QNX Neutrino SDP.
+        // Only positive values should be used, see e.g.
+        // https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/s/setuid.html
+        type UserId = i32;
+        type GroupId = i32;
+    } else {
+        type UserId = u32;
+        type GroupId = u32;
+    }
+}
 
 /// Unix-specific extensions to the [`process::Command`] builder.
 ///
diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs
index ae4faf27b4d..95967eac295 100644
--- a/library/std/src/os/unix/ucred.rs
+++ b/library/std/src/os/unix/ucred.rs
@@ -79,7 +79,8 @@ pub mod impl_linux {
     target_os = "dragonfly",
     target_os = "freebsd",
     target_os = "openbsd",
-    target_os = "netbsd"
+    target_os = "netbsd",
+    target_os = "nto",
 ))]
 pub mod impl_bsd {
     use super::UCred;
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index a5ce6d5120d..3d79058b320 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -69,7 +69,8 @@ impl DoubleEndedIterator for Args {
     target_os = "fuchsia",
     target_os = "redox",
     target_os = "vxworks",
-    target_os = "horizon"
+    target_os = "horizon",
+    target_os = "nto",
 ))]
 mod imp {
     use super::Args;
diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs
index c9ba661c829..1a9276f1110 100644
--- a/library/std/src/sys/unix/env.rs
+++ b/library/std/src/sys/unix/env.rs
@@ -185,6 +185,17 @@ pub mod os {
     pub const EXE_EXTENSION: &str = "";
 }
 
+#[cfg(target_os = "nto")]
+pub mod os {
+    pub const FAMILY: &str = "unix";
+    pub const OS: &str = "nto";
+    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 = "redox")]
 pub mod os {
     pub const FAMILY: &str = "unix";
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index dbaa3c33e2e..a2efd79570d 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -53,7 +53,12 @@ const fn max_iov() -> usize {
     libc::IOV_MAX as usize
 }
 
-#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
+#[cfg(any(
+    target_os = "android",
+    target_os = "emscripten",
+    target_os = "linux",
+    target_os = "nto",
+))]
 const fn max_iov() -> usize {
     libc::UIO_MAXIOV as usize
 }
@@ -67,6 +72,7 @@ const fn max_iov() -> usize {
     target_os = "linux",
     target_os = "macos",
     target_os = "netbsd",
+    target_os = "nto",
     target_os = "openbsd",
     target_os = "horizon",
     target_os = "watchos",
@@ -212,7 +218,8 @@ impl FileDesc {
         target_os = "linux",
         target_os = "haiku",
         target_os = "redox",
-        target_os = "vxworks"
+        target_os = "vxworks",
+        target_os = "nto",
     )))]
     pub fn set_cloexec(&self) -> io::Result<()> {
         unsafe {
@@ -230,7 +237,8 @@ impl FileDesc {
         target_os = "linux",
         target_os = "haiku",
         target_os = "redox",
-        target_os = "vxworks"
+        target_os = "vxworks",
+        target_os = "nto",
     ))]
     pub fn set_cloexec(&self) -> io::Result<()> {
         unsafe {
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 8e1f35d6cc9..bdccb784674 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -13,7 +13,8 @@ use crate::mem;
     target_os = "solaris",
     target_os = "fuchsia",
     target_os = "redox",
-    target_os = "illumos"
+    target_os = "illumos",
+    target_os = "nto",
 ))]
 use crate::mem::MaybeUninit;
 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
@@ -54,7 +55,8 @@ use libc::fstatat64;
     target_os = "solaris",
     target_os = "fuchsia",
     target_os = "redox",
-    target_os = "illumos"
+    target_os = "illumos",
+    target_os = "nto",
 ))]
 use libc::readdir as readdir64;
 #[cfg(target_os = "linux")]
@@ -69,7 +71,8 @@ use libc::readdir64_r;
     target_os = "illumos",
     target_os = "l4re",
     target_os = "fuchsia",
-    target_os = "redox"
+    target_os = "redox",
+    target_os = "nto",
 )))]
 use libc::readdir_r as readdir64_r;
 #[cfg(target_os = "android")]
@@ -277,7 +280,8 @@ unsafe impl Sync for Dir {}
     target_os = "solaris",
     target_os = "illumos",
     target_os = "fuchsia",
-    target_os = "redox"
+    target_os = "redox",
+    target_os = "nto",
 ))]
 pub struct DirEntry {
     dir: Arc<InnerReadDir>,
@@ -297,11 +301,12 @@ pub struct DirEntry {
     target_os = "solaris",
     target_os = "illumos",
     target_os = "fuchsia",
-    target_os = "redox"
+    target_os = "redox",
+    target_os = "nto",
 ))]
 struct dirent64_min {
     d_ino: u64,
-    #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+    #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "nto")))]
     d_type: u8,
 }
 
@@ -311,7 +316,8 @@ struct dirent64_min {
     target_os = "solaris",
     target_os = "illumos",
     target_os = "fuchsia",
-    target_os = "redox"
+    target_os = "redox",
+    target_os = "nto",
 )))]
 pub struct DirEntry {
     dir: Arc<InnerReadDir>,
@@ -438,7 +444,7 @@ impl FileAttr {
     }
 }
 
-#[cfg(not(target_os = "netbsd"))]
+#[cfg(not(any(target_os = "netbsd", target_os = "nto")))]
 impl FileAttr {
     #[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
     pub fn modified(&self) -> io::Result<SystemTime> {
@@ -524,6 +530,21 @@ impl FileAttr {
     }
 }
 
+#[cfg(target_os = "nto")]
+impl FileAttr {
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec))
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec))
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec))
+    }
+}
+
 impl AsInner<stat64> for FileAttr {
     fn as_inner(&self) -> &stat64 {
         &self.stat
@@ -603,7 +624,8 @@ impl Iterator for ReadDir {
         target_os = "solaris",
         target_os = "fuchsia",
         target_os = "redox",
-        target_os = "illumos"
+        target_os = "illumos",
+        target_os = "nto",
     ))]
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
         if self.end_of_stream {
@@ -686,7 +708,11 @@ impl Iterator for ReadDir {
 
                 let entry = dirent64_min {
                     d_ino: *offset_ptr!(entry_ptr, d_ino) as u64,
-                    #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
+                    #[cfg(not(any(
+                        target_os = "solaris",
+                        target_os = "illumos",
+                        target_os = "nto",
+                    )))]
                     d_type: *offset_ptr!(entry_ptr, d_type) as u8,
                 };
 
@@ -705,7 +731,8 @@ impl Iterator for ReadDir {
         target_os = "solaris",
         target_os = "fuchsia",
         target_os = "redox",
-        target_os = "illumos"
+        target_os = "illumos",
+        target_os = "nto",
     )))]
     fn next(&mut self) -> Option<io::Result<DirEntry>> {
         if self.end_of_stream {
@@ -794,7 +821,8 @@ impl DirEntry {
         target_os = "solaris",
         target_os = "illumos",
         target_os = "haiku",
-        target_os = "vxworks"
+        target_os = "vxworks",
+        target_os = "nto",
     ))]
     pub fn file_type(&self) -> io::Result<FileType> {
         self.metadata().map(|m| m.file_type())
@@ -804,7 +832,8 @@ impl DirEntry {
         target_os = "solaris",
         target_os = "illumos",
         target_os = "haiku",
-        target_os = "vxworks"
+        target_os = "vxworks",
+        target_os = "nto",
     )))]
     pub fn file_type(&self) -> io::Result<FileType> {
         match self.entry.d_type {
@@ -834,7 +863,8 @@ impl DirEntry {
         target_os = "redox",
         target_os = "vxworks",
         target_os = "espidf",
-        target_os = "horizon"
+        target_os = "horizon",
+        target_os = "nto",
     ))]
     pub fn ino(&self) -> u64 {
         self.entry.d_ino as u64
@@ -887,7 +917,8 @@ impl DirEntry {
         target_os = "solaris",
         target_os = "illumos",
         target_os = "fuchsia",
-        target_os = "redox"
+        target_os = "redox",
+        target_os = "nto",
     )))]
     fn name_cstr(&self) -> &CStr {
         unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
@@ -898,7 +929,8 @@ impl DirEntry {
         target_os = "solaris",
         target_os = "illumos",
         target_os = "fuchsia",
-        target_os = "redox"
+        target_os = "redox",
+        target_os = "nto",
     ))]
     fn name_cstr(&self) -> &CStr {
         &self.name
@@ -1051,7 +1083,8 @@ impl File {
             target_os = "linux",
             target_os = "android",
             target_os = "netbsd",
-            target_os = "openbsd"
+            target_os = "openbsd",
+            target_os = "nto",
         ))]
         unsafe fn os_datasync(fd: c_int) -> c_int {
             libc::fdatasync(fd)
@@ -1065,6 +1098,7 @@ impl File {
             target_os = "netbsd",
             target_os = "openbsd",
             target_os = "watchos",
+            target_os = "nto",
         )))]
         unsafe fn os_datasync(fd: c_int) -> c_int {
             libc::fsync(fd)
@@ -1750,13 +1784,25 @@ pub fn chroot(dir: &Path) -> io::Result<()> {
 pub use remove_dir_impl::remove_dir_all;
 
 // Fallback for REDOX, ESP-ID, Horizon, and Miri
-#[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri))]
+#[cfg(any(
+    target_os = "redox",
+    target_os = "espidf",
+    target_os = "horizon",
+    target_os = "nto",
+    miri
+))]
 mod remove_dir_impl {
     pub use crate::sys_common::fs::remove_dir_all;
 }
 
 // Modern implementation using openat(), unlinkat() and fdopendir()
-#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))]
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "espidf",
+    target_os = "horizon",
+    target_os = "nto",
+    miri
+)))]
 mod remove_dir_impl {
     use super::{lstat, Dir, DirEntry, InnerReadDir, ReadDir};
     use crate::ffi::CStr;
diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs
index 6be1abc2b08..192fa216dfa 100644
--- a/library/std/src/sys/unix/locks/pthread_condvar.rs
+++ b/library/std/src/sys/unix/locks/pthread_condvar.rs
@@ -2,7 +2,10 @@ use crate::cell::UnsafeCell;
 use crate::ptr;
 use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
 use crate::sys::locks::{pthread_mutex, Mutex};
+#[cfg(not(target_os = "nto"))]
 use crate::sys::time::TIMESPEC_MAX;
+#[cfg(target_os = "nto")]
+use crate::sys::time::TIMESPEC_MAX_CAPPED;
 use crate::sys_common::lazy_box::{LazyBox, LazyInit};
 use crate::time::Duration;
 
@@ -132,10 +135,18 @@ impl Condvar {
         let mutex = pthread_mutex::raw(mutex);
         self.verify(mutex);
 
+        #[cfg(not(target_os = "nto"))]
         let timeout = Timespec::now(libc::CLOCK_MONOTONIC)
             .checked_add_duration(&dur)
             .and_then(|t| t.to_timespec())
             .unwrap_or(TIMESPEC_MAX);
+
+        #[cfg(target_os = "nto")]
+        let timeout = Timespec::now(libc::CLOCK_MONOTONIC)
+            .checked_add_duration(&dur)
+            .and_then(|t| t.to_timespec_capped())
+            .unwrap_or(TIMESPEC_MAX_CAPPED);
+
         let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
         assert!(r == libc::ETIMEDOUT || r == 0);
         r == 0
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index c86f80972a6..8e05b618daa 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -78,6 +78,7 @@ impl Socket {
                     target_os = "linux",
                     target_os = "netbsd",
                     target_os = "openbsd",
+                    target_os = "nto",
                 ))] {
                     // On platforms that support it we pass the SOCK_CLOEXEC
                     // flag to atomically create the socket and set it as
@@ -115,6 +116,7 @@ impl Socket {
                     target_os = "linux",
                     target_os = "netbsd",
                     target_os = "openbsd",
+                    target_os = "nto",
                 ))] {
                     // Like above, set cloexec atomically
                     cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 2f2663db607..21b035fb373 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -62,6 +62,7 @@ extern "C" {
         link_name = "__errno"
     )]
     #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")]
+    #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")]
     #[cfg_attr(
         any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"),
         link_name = "__error"
@@ -361,6 +362,17 @@ pub fn current_exe() -> io::Result<PathBuf> {
     }
 }
 
+#[cfg(target_os = "nto")]
+pub fn current_exe() -> io::Result<PathBuf> {
+    let mut e = crate::fs::read("/proc/self/exefile")?;
+    // Current versions of QNX Neutrino provide a null-terminated path.
+    // Ensure the trailing null byte is not returned here.
+    if let Some(0) = e.last() {
+        e.pop();
+    }
+    Ok(PathBuf::from(OsString::from_vec(e)))
+}
+
 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
 pub fn current_exe() -> io::Result<PathBuf> {
     unsafe {
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 3bc17b7754d..ebdcb5acf22 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -18,6 +18,7 @@ use crate::sys::weak::raw_syscall;
     target_os = "freebsd",
     all(target_os = "linux", target_env = "gnu"),
     all(target_os = "linux", target_env = "musl"),
+    target_os = "nto",
 ))]
 use crate::sys::weak::weak;
 
@@ -140,7 +141,7 @@ impl Command {
 
     // Attempts to fork the process. If successful, returns Ok((0, -1))
     // in the child, and Ok((child_pid, -1)) in the parent.
-    #[cfg(not(target_os = "linux"))]
+    #[cfg(not(target_os = "linux", target_os = "nto"))]
     unsafe fn do_fork(&mut self) -> Result<(pid_t, pid_t), io::Error> {
         cvt(libc::fork()).map(|res| (res, -1))
     }
@@ -389,6 +390,7 @@ impl Command {
         target_os = "freebsd",
         all(target_os = "linux", target_env = "gnu"),
         all(target_os = "linux", target_env = "musl"),
+        target_os = "nto",
     )))]
     fn posix_spawn(
         &mut self,
@@ -405,6 +407,7 @@ impl Command {
         target_os = "freebsd",
         all(target_os = "linux", target_env = "gnu"),
         all(target_os = "linux", target_env = "musl"),
+        target_os = "nto",
     ))]
     fn posix_spawn(
         &mut self,
@@ -760,7 +763,7 @@ fn signal_string(signal: i32) -> &'static str {
             )
         ))]
         libc::SIGSTKFLT => " (SIGSTKFLT)",
-        #[cfg(target_os = "linux")]
+        #[cfg(any(target_os = "linux", target_os = "nto"))]
         libc::SIGPWR => " (SIGPWR)",
         #[cfg(any(
             target_os = "macos",
@@ -769,7 +772,8 @@ fn signal_string(signal: i32) -> &'static str {
             target_os = "freebsd",
             target_os = "netbsd",
             target_os = "openbsd",
-            target_os = "dragonfly"
+            target_os = "dragonfly",
+            target_os = "nto",
         ))]
         libc::SIGEMT => " (SIGEMT)",
         #[cfg(any(
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index cc0e5929569..15070b1f6a7 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -9,7 +9,7 @@ use crate::time::Duration;
 
 #[cfg(all(target_os = "linux", target_env = "gnu"))]
 use crate::sys::weak::dlsym;
-#[cfg(any(target_os = "solaris", target_os = "illumos"))]
+#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
 use crate::sys::weak::weak;
 #[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))]
 pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -173,7 +173,7 @@ impl Thread {
         }
     }
 
-    #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+    #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))]
     pub fn set_name(name: &CStr) {
         weak! {
             fn pthread_setname_np(
@@ -381,6 +381,17 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
             }
 
             Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
+        } else if #[cfg(target_os = "nto")] {
+            unsafe {
+                use libc::_syspage_ptr;
+                if _syspage_ptr.is_null() {
+                    Err(io::const_io_error!(io::ErrorKind::NotFound, "No syspage available"))
+                } else {
+                    let cpus = (*_syspage_ptr).num_cpu;
+                    NonZeroUsize::new(cpus as usize)
+                        .ok_or(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"))
+                }
+            }
         } else if #[cfg(target_os = "haiku")] {
             // system_info cpu_count field gets the static data set at boot time with `smp_set_num_cpus`
             // `get_system_info` calls then `smp_get_num_cpus`
diff --git a/library/std/src/sys/unix/thread_parking/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs
index 082d25e68f5..43046ed07b8 100644
--- a/library/std/src/sys/unix/thread_parking/pthread.rs
+++ b/library/std/src/sys/unix/thread_parking/pthread.rs
@@ -6,7 +6,10 @@ use crate::pin::Pin;
 use crate::ptr::addr_of_mut;
 use crate::sync::atomic::AtomicUsize;
 use crate::sync::atomic::Ordering::SeqCst;
+#[cfg(not(target_os = "nto"))]
 use crate::sys::time::TIMESPEC_MAX;
+#[cfg(target_os = "nto")]
+use crate::sys::time::TIMESPEC_MAX_CAPPED;
 use crate::time::Duration;
 
 const EMPTY: usize = 0;
@@ -80,8 +83,14 @@ unsafe fn wait_timeout(
         (Timespec::now(libc::CLOCK_MONOTONIC), dur)
     };
 
+    #[cfg(not(target_os = "nto"))]
     let timeout =
         now.checked_add_duration(&dur).and_then(|t| t.to_timespec()).unwrap_or(TIMESPEC_MAX);
+    #[cfg(target_os = "nto")]
+    let timeout = now
+        .checked_add_duration(&dur)
+        .and_then(|t| t.to_timespec_capped())
+        .unwrap_or(TIMESPEC_MAX_CAPPED);
     let r = libc::pthread_cond_timedwait(cond, lock, &timeout);
     debug_assert!(r == libc::ETIMEDOUT || r == 0);
 }
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 2daad981b73..0f11de8f5b8 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -9,6 +9,14 @@ pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
 pub const TIMESPEC_MAX: libc::timespec =
     libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
 
+// This additional constant is only used when calling
+// `libc::pthread_cond_timedwait`.
+#[cfg(target_os = "nto")]
+pub(super) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
+    tv_sec: (u64::MAX / NSEC_PER_SEC) as i64,
+    tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64,
+};
+
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(transparent)]
 #[rustc_layout_scalar_valid_range_start(0)]
@@ -144,6 +152,20 @@ impl Timespec {
             tv_nsec: self.tv_nsec.0.try_into().ok()?,
         })
     }
+
+    // On QNX Neutrino, the maximum timespec for e.g. pthread_cond_timedwait
+    // is 2^64 nanoseconds
+    #[cfg(target_os = "nto")]
+    pub(super) fn to_timespec_capped(&self) -> Option<libc::timespec> {
+        // Check if timeout in nanoseconds would fit into an u64
+        if (self.tv_nsec.0 as u64)
+            .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?)
+            .is_none()
+        {
+            return None;
+        }
+        self.to_timespec()
+    }
 }
 
 impl From<libc::timespec> for Timespec {
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index f1d804ef40c..8752f46ff81 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -91,6 +91,19 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
         if stop {
             return false;
         }
+        #[cfg(target_os = "nto")]
+        if libc::__my_thread_exit as *mut libc::c_void == frame.ip() {
+            if !hit && start {
+                use crate::backtrace_rs::SymbolName;
+                res = bt_fmt.frame().print_raw(
+                    frame.ip(),
+                    Some(SymbolName::new("__my_thread_exit".as_bytes())),
+                    None,
+                    None,
+                );
+            }
+            return false;
+        }
         if !hit && start {
             res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
         }
diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs
index 2c38dfecf97..85ecc1def3a 100644
--- a/library/std/src/sys_common/net.rs
+++ b/library/std/src/sys_common/net.rs
@@ -21,7 +21,7 @@ cfg_if::cfg_if! {
         target_os = "dragonfly", target_os = "freebsd",
         target_os = "ios", target_os = "macos", target_os = "watchos",
         target_os = "openbsd", target_os = "netbsd", target_os = "illumos",
-        target_os = "solaris", target_os = "haiku", target_os = "l4re"))] {
+        target_os = "solaris", target_os = "haiku", target_os = "l4re", target_os = "nto"))] {
         use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP;
         use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP;
     } else {
@@ -35,7 +35,7 @@ cfg_if::cfg_if! {
         target_os = "linux", target_os = "android",
         target_os = "dragonfly", target_os = "freebsd",
         target_os = "openbsd", target_os = "netbsd",
-        target_os = "haiku"))] {
+        target_os = "haiku", target_os = "nto"))] {
         use libc::MSG_NOSIGNAL;
     } else {
         const MSG_NOSIGNAL: c_int = 0x0;
@@ -46,7 +46,8 @@ cfg_if::cfg_if! {
     if #[cfg(any(
         target_os = "dragonfly", target_os = "freebsd",
         target_os = "openbsd", target_os = "netbsd",
-        target_os = "solaris", target_os = "illumos"))] {
+        target_os = "solaris", target_os = "illumos",
+        target_os = "nto"))] {
         use crate::ffi::c_uchar;
         type IpV4MultiCastType = c_uchar;
     } else {
diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs
index 2672a2a75b0..89360e45601 100644
--- a/library/std/src/sys_common/thread_local_key.rs
+++ b/library/std/src/sys_common/thread_local_key.rs
@@ -117,9 +117,15 @@ pub struct Key {
 /// This value specifies no destructor by default.
 pub const INIT: StaticKey = StaticKey::new(None);
 
-// Define a sentinel value that is unlikely to be returned
-// as a TLS key (but it may be returned).
+// Define a sentinel value that is likely not to be returned
+// as a TLS key.
+#[cfg(not(target_os = "nto"))]
 const KEY_SENTVAL: usize = 0;
+// On QNX Neutrino, 0 is always returned when currently not in use.
+// Using 0 would mean to always create two keys and remote the first
+// one (with value of 0) immediately afterwards.
+#[cfg(target_os = "nto")]
+const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1;
 
 impl StaticKey {
     #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs
index 3753071d5f0..edc10aa39af 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -127,3 +127,7 @@ extern "C" {}
 #[cfg(target_os = "haiku")]
 #[link(name = "gcc_s")]
 extern "C" {}
+
+#[cfg(target_os = "nto")]
+#[link(name = "gcc_s")]
+extern "C" {}
diff --git a/src/doc/rustc/src/platform-support/nto-qnx.md b/src/doc/rustc/src/platform-support/nto-qnx.md
index 37d0c31976c..38198fe6c3a 100644
--- a/src/doc/rustc/src/platform-support/nto-qnx.md
+++ b/src/doc/rustc/src/platform-support/nto-qnx.md
@@ -2,9 +2,9 @@
 
 **Tier: 3**
 
-[BlackBerry® QNX®][BlackBerry] Neutrino (nto) Real-time operating system.
+[QNX®][BlackBerry] Neutrino (nto) Real-time operating system.
 The support has been implemented jointly by [Elektrobit Automotive GmbH][Elektrobit]
-and [BlackBerry][BlackBerry].
+and [Blackberry QNX][BlackBerry].
 
 [BlackBerry]: https://blackberry.qnx.com
 [Elektrobit]: https://www.elektrobit.com
@@ -19,19 +19,24 @@ and [BlackBerry][BlackBerry].
 Currently, only cross-compilation for QNX Neutrino on AArch64 and x86_64 are supported (little endian).
 Adding other architectures that are supported by QNX Neutrino is possible.
 
-The standard library does not yet support QNX Neutrino. Therefore, only `no_std` code can
-be compiled.
+The standard library, including `core` and `alloc` (with default allocator) are supported.
 
-`core` and `alloc` (with default allocator) are supported.
+For building or using the Rust toolchain for QNX Neutrino, the
+[QNX Software Development Platform (SDP)](https://blackberry.qnx.com/en/products/foundation-software/qnx-software-development-platform)
+must be installed and initialized.
+Initialization is usually done by sourcing `qnxsdp-env.sh` (this will be installed as part of the SDP, see also installation instruction provided with the SDP).
+Afterwards [`qcc`](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.utilities/topic/q/qcc.html) (QNX C/C++ compiler)
+should be available (in the `$PATH` variable).
+`qcc` will be called e.g. for linking executables.
 
-Applications must link against `libc.so` (see example). This is required because applications
-always link against the `crt` library and `crt` depends on `libc.so`.
-
-The correct version of `qcc` must be available by setting the `$PATH` variable (e.g. by sourcing `qnxsdp-env.sh` of the
-QNX Neutrino toolchain).
+When linking `no_std` applications, they must link against `libc.so` (see example). This is
+required because applications always link against the `crt` library and `crt` depends on `libc.so`.
+This is done automatically when using the standard library.
 
 ### Small example application
 
+Small `no_std` example is shown below. Applications using the standard library work as well.
+
 ```rust,ignore (platform-specific)
 #![no_std]
 #![no_main]
@@ -89,30 +94,150 @@ changelog-seen = 2
 
 2. Compile the Rust toolchain for an `x86_64-unknown-linux-gnu` host (for both `aarch64` and `x86_64` targets)
 
-Run the following:
+Compiling the Rust toolchain requires the same environment variables used for compiling C binaries.
+Refer to the [QNX developer manual](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.prog/topic/devel_OS_version.html).
+
+To compile for QNX Neutrino (aarch64 and x86_64) and Linux (x86_64):
 
 ```bash
-env \
-    CC_aarch64-unknown-nto-qnx710="qcc" \
-    CFLAGS_aarch64-unknown-nto-qnx710="-Vgcc_ntoaarch64le_cxx" \
-    CXX_aarch64-unknown-nto-qnx710="qcc" \
-    AR_aarch64_unknown_nto_qnx710="ntoaarch64-ar" \
-    CC_x86_64-pc-nto-qnx710="qcc" \
-    CFLAGS_x86_64-pc-nto-qnx710="-Vgcc_ntox86_64_cxx" \
-    CXX_x86_64-pc-nto-qnx710="qcc" \
-    AR_x86_64_pc_nto_qnx710="ntox86_64-ar" \
-        ./x.py build --target aarch64-unknown-nto-qnx710 --target x86_64-pc-nto-qnx710 --target x86_64-unknown-linux-gnu rustc library/core library/alloc/
+export build_env='
+    CC_aarch64-unknown-nto-qnx710=qcc
+    CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx
+    CXX_aarch64-unknown-nto-qnx710=qcc
+    AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar
+    CC_x86_64-pc-nto-qnx710=qcc
+    CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx
+    CXX_x86_64-pc-nto-qnx710=qcc
+    AR_x86_64_pc_nto_qnx710=ntox86_64-ar'
+
+env $build_env \
+    ./x.py build \
+        --target aarch64-unknown-nto-qnx710 \
+        --target x86_64-pc-nto-qnx710 \
+        --target x86_64-unknown-linux-gnu \
+        rustc library/core library/alloc
 ```
 
+## Running the Rust test suite
+
+The test suites of the Rust compiler and standard library can be executed much like other Rust targets.
+The environment for testing should match the one used during compiler compilation (refer to `build_env` and `qcc`/`PATH` above) with the
+addition of the TEST_DEVICE_ADDR environment variable.
+The TEST_DEVICE_ADDR variable controls the remote runner and should point to the target, despite localhost being shown in the following example.
+Note that some tests are failing which is why they are currently excluded by the target maintainers which can be seen in the following example.
+
+To run all tests on a x86_64 QNX Neutrino target:
+
+```bash
+export TEST_DEVICE_ADDR="localhost:12345" # must address the test target, can be a SSH tunnel
+export build_env='
+    CC_aarch64-unknown-nto-qnx710=qcc
+    CFLAGS_aarch64-unknown-nto-qnx710=-Vgcc_ntoaarch64le_cxx
+    CXX_aarch64-unknown-nto-qnx710=qcc
+    AR_aarch64_unknown_nto_qnx710=ntoaarch64-ar
+    CC_x86_64-pc-nto-qnx710=qcc
+    CFLAGS_x86_64-pc-nto-qnx710=-Vgcc_ntox86_64_cxx
+    CXX_x86_64-pc-nto-qnx710=qcc
+    AR_x86_64_pc_nto_qnx710=ntox86_64-ar'
+
+# Disable tests that only work on the host or don't make sense for this target.
+# See also:
+# - src/ci/docker/host-x86_64/i686-gnu/Dockerfile
+# - https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Running.20tests.20on.20remote.20target
+# - .github/workflows/ci.yml
+export exclude_tests='
+    --exclude src/bootstrap
+    --exclude src/tools/error_index_generator
+    --exclude src/tools/linkchecker
+    --exclude tests/ui-fulldeps
+    --exclude rustc
+    --exclude rustdoc
+    --exclude tests/run-make-fulldeps'
+
+env $build_env \
+    ./x.py test -j 1 \
+        $exclude_tests \
+        --stage 1 \
+        --target x86_64-pc-nto-qnx710
+```
+
+Currently, only one thread can be used when testing due to limitations in `libc::fork` and `libc::posix_spawnp`.
+See [fork documentation](https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.lib_ref/topic/f/fork.html)
+(error section) for more information.
+This can be achieved by using the `-j 1` parameter in the `x.py` call.
+This issue is being researched and we will try to allow parallelism in the future.
+
 ## Building Rust programs
 
-Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you must either build Rust with the target enabled (see "Building the target" above), or build your own copy of  `core` by using
-`build-std` or similar.
+Rust does not yet ship pre-compiled artifacts for this target.
+To compile for this target, you must either build Rust with the target enabled (see "Building the target" above),
+or build your own copy of `core` by using `build-std` or similar.
 
 ## Testing
 
-Compiled executables can directly be run on QNX Neutrino.
+Compiled executables can run directly on QNX Neutrino.
+
+### Rust std library test suite
+
+The target needs sufficient resources to execute all tests. The commands below assume that a QEMU image
+is used.
+
+* Ensure that the temporary directory used by `remote-test-server` has enough free space and inodes.
+  5GB of free space and 40000 inodes are known to be sufficient (the test will create more than 32k files).
+  To create a QEMU image in an empty directory, run this command inside the directory:
+
+  ```bash
+  mkqnximage --type=qemu --ssh-ident=$HOME/.ssh/id_ed25519.pub --data-size=5000 --data-inodes=40000
+  ```
+
+  `/data` should have enough free resources.
+  Set the `TMPDIR` environment variable accordingly when running `remote-test-server`, e.g.:
+  ```bash
+  TMPDIR=/data/tmp/rust remote-test-server --bind 0.0.0.0:12345
+  ```
+
+* Ensure the TCP stack can handle enough parallel connections (default is 200, should be 300 or higher).
+  After creating an image (see above), edit the file `output/build/startup.sh`:
+  1. Search for `io-pkt-v6-hc`
+  2. Add the parameter `-ptcpip threads_max=300`, e.g.:
+     ```text
+     io-pkt-v6-hc -U 33:33 -d e1000 -ptcpip threads_max=300
+     ```
+  3. Update the image by running `mkqnximage` again with the same parameters as above for creating it.
+
+* Running and stopping the virtual machine
+
+  To start the virtual machine, run inside the directory of the VM:
+
+  ```bash
+  mkqnximage --run=-h
+  ```
+
+  To stop the virtual machine, run inside the directory of the VM:
+
+  ```bash
+  mkqnximage --stop
+  ```
+
+* Ensure local networking
+
+  Ensure that 'localhost' is getting resolved to 127.0.0.1. If you can't ping the localhost, some tests may fail.
+  Ensure it's appended to /etc/hosts (if first `ping` command fails).
+  Commands have to be executed inside the virtual machine!
+
+  ```bash
+  $ ping localhost
+  ping: Cannot resolve "localhost" (Host name lookup failure)
+
+  $ echo "127.0.0.1 localhost" >> /etc/hosts
+
+  $ ping localhost
+  PING localhost (127.0.0.1): 56 data bytes
+  64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=1 ms
+  ```
 
 ## Cross-compilation toolchains and C code
 
-Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above), to ensure `qcc` is used with proper arguments. To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout.
+Compiling C code requires the same environment variables to be set as compiling the Rust toolchain (see above),
+to ensure `qcc` is used with proper arguments.
+To ensure compatibility, do not specify any further arguments that for example change calling conventions or memory layout.
diff --git a/tests/codegen/thread-local.rs b/tests/codegen/thread-local.rs
index 0f1b29ca79b..aa7fab7fb17 100644
--- a/tests/codegen/thread-local.rs
+++ b/tests/codegen/thread-local.rs
@@ -4,6 +4,7 @@
 // ignore-wasm globals are used instead of thread locals
 // ignore-emscripten globals are used instead of thread locals
 // ignore-android does not use #[thread_local]
+// ignore-nto does not use #[thread_local]
 
 #![crate_type = "lib"]
 
diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs
index 6d934538f4c..a455eef42ea 100644
--- a/tests/ui/abi/stack-probes-lto.rs
+++ b/tests/ui/abi/stack-probes-lto.rs
@@ -13,5 +13,6 @@
 // ignore-fuchsia no exception handler registered for segfault
 // compile-flags: -C lto
 // no-prefer-dynamic
+// ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino
 
 include!("stack-probes.rs");
diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs
index e7b91644b3b..8dba54c3f81 100644
--- a/tests/ui/abi/stack-probes.rs
+++ b/tests/ui/abi/stack-probes.rs
@@ -9,6 +9,7 @@
 // ignore-emscripten no processes
 // ignore-sgx no processes
 // ignore-fuchsia no exception handler registered for segfault
+// ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino
 
 use std::env;
 use std::mem::MaybeUninit;
diff --git a/tests/ui/command/command-setgroups.rs b/tests/ui/command/command-setgroups.rs
index aff67f91bba..7e321f2f0cd 100644
--- a/tests/ui/command/command-setgroups.rs
+++ b/tests/ui/command/command-setgroups.rs
@@ -3,6 +3,7 @@
 // ignore-emscripten
 // ignore-sgx
 // ignore-musl - returns dummy result for _SC_NGROUPS_MAX
+// ignore-nto - does not have `/bin/id`, expects groups to be i32 (not u32)
 
 #![feature(rustc_private)]
 #![feature(setgroups)]
diff --git a/tests/ui/intrinsics/intrinsic-alignment.rs b/tests/ui/intrinsics/intrinsic-alignment.rs
index c8b1ff1dbce..b99bb39d062 100644
--- a/tests/ui/intrinsics/intrinsic-alignment.rs
+++ b/tests/ui/intrinsics/intrinsic-alignment.rs
@@ -22,7 +22,9 @@ mod rusti {
           target_os = "netbsd",
           target_os = "openbsd",
           target_os = "solaris",
-          target_os = "vxworks"))]
+          target_os = "vxworks",
+          target_os = "nto",
+))]
 mod m {
     #[cfg(target_arch = "x86")]
     pub fn main() {
diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs
index 107eba45dc2..7ae14c6b84d 100644
--- a/tests/ui/process/process-sigpipe.rs
+++ b/tests/ui/process/process-sigpipe.rs
@@ -15,6 +15,7 @@
 // ignore-emscripten no threads support
 // ignore-vxworks no 'sh'
 // ignore-fuchsia no 'sh'
+// ignore-nto no 'yes'
 
 use std::process;
 use std::thread;
diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs
index 6873abc49b2..ff45ace7857 100644
--- a/tests/ui/runtime/out-of-stack.rs
+++ b/tests/ui/runtime/out-of-stack.rs
@@ -6,6 +6,7 @@
 // ignore-emscripten no processes
 // ignore-sgx no processes
 // ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590)
+// ignore-nto no stack overflow handler used (no alternate stack available)
 
 #![feature(core_intrinsics)]
 #![feature(rustc_private)]
diff --git a/tests/ui/runtime/signal-alternate-stack-cleanup.rs b/tests/ui/runtime/signal-alternate-stack-cleanup.rs
index 8a6d738959e..37c602ae0b0 100644
--- a/tests/ui/runtime/signal-alternate-stack-cleanup.rs
+++ b/tests/ui/runtime/signal-alternate-stack-cleanup.rs
@@ -7,6 +7,7 @@
 // ignore-windows
 // ignore-sgx no libc
 // ignore-vxworks no SIGWINCH in user space
+// ignore-nto no SA_ONSTACK
 
 #![feature(rustc_private)]
 extern crate libc;
diff --git a/tests/ui/structs-enums/rec-align-u64.rs b/tests/ui/structs-enums/rec-align-u64.rs
index 40ede9705f1..f21c9b2c808 100644
--- a/tests/ui/structs-enums/rec-align-u64.rs
+++ b/tests/ui/structs-enums/rec-align-u64.rs
@@ -43,7 +43,9 @@ struct Outer {
           target_os = "netbsd",
           target_os = "openbsd",
           target_os = "solaris",
-          target_os = "vxworks"))]
+          target_os = "vxworks",
+          target_os = "nto",
+))]
 mod m {
     #[cfg(target_arch = "x86")]
     pub mod m {
diff --git a/tests/ui/thread-local/tls.rs b/tests/ui/thread-local/tls.rs
index fbd3413885f..f03bd3f991b 100644
--- a/tests/ui/thread-local/tls.rs
+++ b/tests/ui/thread-local/tls.rs
@@ -1,6 +1,7 @@
 // run-pass
 // ignore-emscripten no threads support
 // compile-flags: -O
+// ignore-nto Doesn't work without emulated TLS enabled (in LLVM)
 
 #![feature(thread_local)]
 
diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs
index 674c26a43f2..82a1dd63713 100644
--- a/tests/ui/wait-forked-but-failed-child.rs
+++ b/tests/ui/wait-forked-but-failed-child.rs
@@ -3,6 +3,7 @@
 // ignore-sgx no processes
 // ignore-vxworks no 'ps'
 // ignore-fuchsia no 'ps'
+// ignore-nto no 'ps'
 
 #![feature(rustc_private)]