Add QNX Neutrino support to libstd

Co-authored-by: gh-tr <troach@qnx.com>
This commit is contained in:
Florian Bartels 2023-01-10 10:44:05 +01:00
parent 8f41570e91
commit 3ce2cd059f
No known key found for this signature in database
40 changed files with 534 additions and 74 deletions

View File

@ -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;

View File

@ -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 {

View File

@ -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() {

View File

@ -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();

View File

@ -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")]

View File

@ -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
}
}

View File

@ -0,0 +1,4 @@
#![stable(feature = "raw_ext", since = "1.1.0")]
pub mod fs;
pub mod raw;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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;
use cfg_if::cfg_if;
#[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;
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.
///

View File

@ -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;

View File

@ -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;

View File

@ -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";

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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()))?;

View File

@ -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 {

View File

@ -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(

View File

@ -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`

View File

@ -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);
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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 {

View File

@ -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")]

View File

@ -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" {}

View File

@ -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.

View File

@ -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"]

View File

@ -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");

View File

@ -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;

View File

@ -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)]

View File

@ -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() {

View File

@ -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;

View File

@ -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)]

View File

@ -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;

View File

@ -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 {

View File

@ -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)]

View File

@ -3,6 +3,7 @@
// ignore-sgx no processes
// ignore-vxworks no 'ps'
// ignore-fuchsia no 'ps'
// ignore-nto no 'ps'
#![feature(rustc_private)]