From 0f68d8a24ccd4214380e6e1d3dd6e81defdeb5c2 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Apr 2026 15:58:37 -0700 Subject: [PATCH 1/3] std: add platform support for hexagon-unknown-qurt QuRT is not a unix target, but provides POSIX-compatible threading, file I/O and synchronization. Drop the unix family from the target spec and instead opt into the unix PAL, selecting the unix implementation of each subsystem where QuRT's POSIX layer is sufficient. This follows the pattern used by other unix-adjacent targets. Process spawning, networking and pipes remain unsupported. --- .../src/spec/targets/hexagon_unknown_qurt.rs | 2 +- library/core/src/io/io_slice.rs | 3 +- library/std/src/os/fd/mod.rs | 4 +- library/std/src/os/fd/owned.rs | 30 ++--- library/std/src/os/fd/raw.rs | 2 + library/std/src/os/mod.rs | 3 + library/std/src/os/qurt/ffi.rs | 64 ++++++++++ library/std/src/os/qurt/fs.rs | 109 ++++++++++++++++ library/std/src/os/qurt/io.rs | 8 ++ library/std/src/os/qurt/mod.rs | 21 ++++ library/std/src/os/qurt/raw.rs | 21 ++++ library/std/src/process.rs | 1 + library/std/src/sys/alloc/mod.rs | 8 +- library/std/src/sys/args/unsupported.rs | 5 + library/std/src/sys/backtrace.rs | 7 +- library/std/src/sys/env/mod.rs | 3 +- library/std/src/sys/env/unix.rs | 9 ++ library/std/src/sys/exit.rs | 2 +- library/std/src/sys/fd/mod.rs | 2 +- library/std/src/sys/fd/unix.rs | 41 ++++++- library/std/src/sys/fs/mod.rs | 15 ++- library/std/src/sys/fs/unix.rs | 116 +++++++++++++++--- library/std/src/sys/io/error/mod.rs | 2 +- library/std/src/sys/io/error/unix.rs | 1 + library/std/src/sys/io/mod.rs | 2 +- .../std/src/sys/net/connection/unsupported.rs | 3 + library/std/src/sys/pal/mod.rs | 2 +- library/std/src/sys/pal/unix/mod.rs | 6 + library/std/src/sys/paths/mod.rs | 2 +- library/std/src/sys/paths/unix.rs | 11 +- library/std/src/sys/pipe/unsupported.rs | 2 +- library/std/src/sys/process/mod.rs | 4 +- library/std/src/sys/process/unsupported.rs | 3 + library/std/src/sys/random/mod.rs | 2 + library/std/src/sys/stdio/mod.rs | 2 +- library/std/src/sys/stdio/unix.rs | 2 +- library/std/src/sys/sync/condvar/mod.rs | 2 +- library/std/src/sys/sync/mutex/mod.rs | 2 +- library/std/src/sys/sync/once/mod.rs | 1 + library/std/src/sys/sync/rwlock/mod.rs | 1 + .../std/src/sys/sync/thread_parking/mod.rs | 2 +- library/std/src/sys/thread/mod.rs | 4 +- library/std/src/sys/thread/unix.rs | 11 +- library/std/src/sys/thread_local/mod.rs | 6 +- library/std/src/sys/time/mod.rs | 8 +- library/sysroot/src/lib.rs | 1 + library/test/src/lib.rs | 1 + src/bootstrap/src/core/builder/cargo.rs | 1 + 48 files changed, 491 insertions(+), 69 deletions(-) create mode 100644 library/std/src/os/qurt/ffi.rs create mode 100644 library/std/src/os/qurt/fs.rs create mode 100644 library/std/src/os/qurt/io.rs create mode 100644 library/std/src/os/qurt/mod.rs create mode 100644 library/std/src/os/qurt/raw.rs diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs index dcc92b4bdcc4a..82bf2b11073b3 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs @@ -29,7 +29,7 @@ pub(crate) fn target() -> Target { exe_suffix: ".elf".into(), dynamic_linking: true, executables: true, - families: cvs!["unix"], + families: cvs![], has_thread_local: true, has_rpath: false, crt_static_default: false, diff --git a/library/core/src/io/io_slice.rs b/library/core/src/io/io_slice.rs index 17f4fc4da940a..b8368a95ce503 100644 --- a/library/core/src/io/io_slice.rs +++ b/library/core/src/io/io_slice.rs @@ -8,7 +8,8 @@ cfg_select! { target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", - target_os = "wasi" + target_os = "wasi", + target_os = "qurt" ) => { #[path = "io_slice/repr_iovec.rs"] mod repr; diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index 735f1cf8925fb..ae5b705fd332f 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -13,14 +13,14 @@ mod raw; mod owned; // Implementations for `AsRawFd` etc. for network types. -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] mod net; // Implementation of stdio file descriptor constants. mod stdio; #[cfg(test)] -#[cfg(not(target_os = "l4re"))] +#[cfg(not(any(target_os = "l4re", target_os = "qurt")))] mod tests; // Export the types and traits for the public API. diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 4ed5c43616a29..86ed13dbe6763 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -345,7 +345,7 @@ impl From for fs::File { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl AsFd for crate::net::TcpStream { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -354,7 +354,7 @@ impl AsFd for crate::net::TcpStream { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for OwnedFd { /// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket file descriptor. #[inline] @@ -364,7 +364,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for crate::net::TcpStream { #[inline] fn from(owned_fd: OwnedFd) -> Self { @@ -375,7 +375,7 @@ impl From for crate::net::TcpStream { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl AsFd for crate::net::TcpListener { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -384,7 +384,7 @@ impl AsFd for crate::net::TcpListener { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for OwnedFd { /// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket file descriptor. #[inline] @@ -394,7 +394,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for crate::net::TcpListener { #[inline] fn from(owned_fd: OwnedFd) -> Self { @@ -405,7 +405,7 @@ impl From for crate::net::TcpListener { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl AsFd for crate::net::UdpSocket { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { @@ -414,7 +414,7 @@ impl AsFd for crate::net::UdpSocket { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for OwnedFd { /// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s file descriptor. #[inline] @@ -424,7 +424,7 @@ impl From for OwnedFd { } #[stable(feature = "io_safety", since = "1.63.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for crate::net::UdpSocket { #[inline] fn from(owned_fd: OwnedFd) -> Self { @@ -533,7 +533,7 @@ impl<'a> AsFd for io::StderrLock<'a> { } #[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl AsFd for io::PipeReader { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() @@ -541,7 +541,7 @@ impl AsFd for io::PipeReader { } #[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for OwnedFd { fn from(pipe: io::PipeReader) -> Self { pipe.0.into_inner() @@ -549,7 +549,7 @@ impl From for OwnedFd { } #[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl AsFd for io::PipeWriter { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() @@ -557,7 +557,7 @@ impl AsFd for io::PipeWriter { } #[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for OwnedFd { fn from(pipe: io::PipeWriter) -> Self { pipe.0.into_inner() @@ -565,7 +565,7 @@ impl From for OwnedFd { } #[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for io::PipeReader { fn from(owned_fd: OwnedFd) -> Self { Self(FromInner::from_inner(owned_fd)) @@ -573,7 +573,7 @@ impl From for io::PipeReader { } #[stable(feature = "anonymous_pipe", since = "1.87.0")] -#[cfg(not(target_os = "trusty"))] +#[cfg(not(any(target_os = "trusty", target_os = "qurt")))] impl From for io::PipeWriter { fn from(owned_fd: OwnedFd) -> Self { Self(FromInner::from_inner(owned_fd)) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index a0c96e2836fc5..5801ec8e4f8a0 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -14,6 +14,8 @@ use crate::fs; use crate::io; #[cfg(target_os = "hermit")] use crate::os::hermit::io::OwnedFd; +#[cfg(target_os = "qurt")] +use crate::os::qurt::io::OwnedFd; #[cfg(all(not(target_os = "hermit"), not(target_os = "motor")))] use crate::os::raw; #[cfg(all(doc, not(any(target_arch = "wasm32", target_env = "sgx", target_os = "l4re"))))] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 8068f92bcc93c..9b008c8be2db6 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -73,6 +73,7 @@ cfg_select! { target_os = "trusty", target_os = "wasi", target_os = "motor", + target_os = "qurt", doc ))] pub mod fd; @@ -127,6 +128,8 @@ pub mod nto; pub mod nuttx; #[cfg(target_os = "openbsd")] pub mod openbsd; +#[cfg(target_os = "qurt")] +pub mod qurt; #[cfg(target_os = "redox")] pub mod redox; #[cfg(target_os = "rtems")] diff --git a/library/std/src/os/qurt/ffi.rs b/library/std/src/os/qurt/ffi.rs new file mode 100644 index 0000000000000..dd4c7b6cad235 --- /dev/null +++ b/library/std/src/os/qurt/ffi.rs @@ -0,0 +1,64 @@ +//! QuRT-specific extension to the primitives in the [`std::ffi`] module. +//! +//! [`std::ffi`]: crate::ffi + +#![stable(feature = "raw_ext", since = "1.1.0")] + +use crate::ffi::{OsStr, OsString}; +use crate::mem; +use crate::sys::os_str::Buf; +use crate::sys::{AsInner, FromInner, IntoInner}; + +/// QuRT-specific extensions to [`OsString`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +#[stable(feature = "raw_ext", since = "1.1.0")] +pub impl(self) trait OsStringExt { + /// Creates an [`OsString`] from a byte vector. + #[stable(feature = "raw_ext", since = "1.1.0")] + fn from_vec(vec: Vec) -> Self; + + /// Yields the underlying byte vector of this [`OsString`]. + #[stable(feature = "raw_ext", since = "1.1.0")] + fn into_vec(self) -> Vec; +} + +#[stable(feature = "raw_ext", since = "1.1.0")] +impl OsStringExt for OsString { + #[inline] + fn from_vec(vec: Vec) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + + #[inline] + fn into_vec(self) -> Vec { + self.into_inner().inner + } +} + +/// QuRT-specific extensions to [`OsStr`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +#[stable(feature = "raw_ext", since = "1.1.0")] +pub impl(self) trait OsStrExt { + #[stable(feature = "raw_ext", since = "1.1.0")] + /// Creates an [`OsStr`] from a byte slice. + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the [`OsStr`] slice. + #[stable(feature = "raw_ext", since = "1.1.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "raw_ext", since = "1.1.0")] +impl OsStrExt for OsStr { + #[inline] + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + + #[inline] + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/library/std/src/os/qurt/fs.rs b/library/std/src/os/qurt/fs.rs new file mode 100644 index 0000000000000..bf36f8deef9cc --- /dev/null +++ b/library/std/src/os/qurt/fs.rs @@ -0,0 +1,109 @@ +//! QuRT-specific extensions to primitives in the [`std::fs`] module. +//! +//! [`std::fs`]: crate::fs + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys::AsInner; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +/// +/// QuRT's `stat` has no ownership, block or sub-second fields; the accessors +/// for those always return 0. +#[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; + /// Returns 0 on QuRT (user ownership not supported). + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + /// Returns 0 on QuRT (group ownership not supported). + #[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; + /// Returns 0 on QuRT (nanosecond precision not supported). + #[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; + /// Returns 0 on QuRT (nanosecond precision not supported). + #[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; + /// Returns 0 on QuRT (nanosecond precision not supported). + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + /// Returns 0 on QuRT (block size not tracked). + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + /// Returns 0 on QuRT (block count not tracked). + #[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 { + 0 + } + fn st_gid(&self) -> u32 { + 0 + } + 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_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + 0 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + 0 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + 0 + } + fn st_blksize(&self) -> u64 { + 0 + } + fn st_blocks(&self) -> u64 { + 0 + } +} diff --git a/library/std/src/os/qurt/io.rs b/library/std/src/os/qurt/io.rs new file mode 100644 index 0000000000000..119f4f0944b1b --- /dev/null +++ b/library/std/src/os/qurt/io.rs @@ -0,0 +1,8 @@ +//! QuRT-specific I/O functionality. +//! +//! QuRT supports Unix-like file descriptors through its POSIX compatibility layer. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +#[stable(feature = "rust1", since = "1.0.0")] +pub use crate::os::fd::*; diff --git a/library/std/src/os/qurt/mod.rs b/library/std/src/os/qurt/mod.rs new file mode 100644 index 0000000000000..e006deac5f199 --- /dev/null +++ b/library/std/src/os/qurt/mod.rs @@ -0,0 +1,21 @@ +//! QuRT-specific definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod ffi; +pub mod fs; +pub mod io; +pub mod raw; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::fs::MetadataExt; +} diff --git a/library/std/src/os/qurt/raw.rs b/library/std/src/os/qurt/raw.rs new file mode 100644 index 0000000000000..d88068569fd08 --- /dev/null +++ b/library/std/src/os/qurt/raw.rs @@ -0,0 +1,21 @@ +//! QuRT-specific raw type definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +use core::ffi::c_long; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; +#[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 = c_long; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = c_long; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type pthread_t = libc::pthread_t; diff --git a/library/std/src/process.rs b/library/std/src/process.rs index a4c461737b620..baad8415b8da2 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -158,6 +158,7 @@ target_os = "trusty", target_os = "hermit", target_os = "l4re", + target_os = "qurt", )) ))] mod tests; diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index 66a2c3be33cba..5dec3ca6e64ae 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -64,7 +64,13 @@ unsafe fn realloc_fallback(ptr: *mut u8, old_layout: Layout, new_size: usize) -> } cfg_select! { - any(target_family = "unix", target_os = "wasi", target_os = "teeos", target_os = "trusty") => { + any( + target_family = "unix", + target_os = "qurt", + target_os = "wasi", + target_os = "teeos", + target_os = "trusty" + ) => { mod unix; use unix as imp; } diff --git a/library/std/src/sys/args/unsupported.rs b/library/std/src/sys/args/unsupported.rs index ecffc6d26414b..722b4b3c0bbdc 100644 --- a/library/std/src/sys/args/unsupported.rs +++ b/library/std/src/sys/args/unsupported.rs @@ -3,6 +3,11 @@ use crate::fmt; pub struct Args {} +#[cfg(target_os = "qurt")] +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { + // QuRT goes through the unix PAL's `init`, but has no args to record. +} + pub fn args() -> Args { Args {} } diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 858a95882b39f..6b31a34df50b4 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -202,7 +202,12 @@ pub fn output_filename( use crate::os::unix::prelude::*; Path::new(crate::ffi::OsStr::from_bytes(bytes)).into() } - #[cfg(not(unix))] + #[cfg(target_os = "qurt")] + BytesOrWideString::Bytes(bytes) => { + use crate::os::qurt::prelude::*; + Path::new(crate::ffi::OsStr::from_bytes(bytes)).into() + } + #[cfg(not(any(unix, target_os = "qurt")))] BytesOrWideString::Bytes(bytes) => { Path::new(crate::str::from_utf8(bytes).unwrap_or("")).into() } diff --git a/library/std/src/sys/env/mod.rs b/library/std/src/sys/env/mod.rs index 89856516b6dce..aae1a5a3ed2b1 100644 --- a/library/std/src/sys/env/mod.rs +++ b/library/std/src/sys/env/mod.rs @@ -6,6 +6,7 @@ target_family = "unix", target_os = "hermit", target_os = "motor", + target_os = "qurt", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", target_os = "uefi", @@ -15,7 +16,7 @@ mod common; cfg_select! { - target_family = "unix" => { + any(target_family = "unix", target_os = "qurt") => { mod unix; pub use unix::*; } diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs index f9ecec0c7304f..f12951194c845 100644 --- a/library/std/src/sys/env/unix.rs +++ b/library/std/src/sys/env/unix.rs @@ -5,6 +5,9 @@ use libc::c_char; pub use super::common::Env; use crate::ffi::{CStr, OsStr, OsString}; use crate::io; +#[cfg(target_os = "qurt")] +use crate::os::qurt::prelude::*; +#[cfg(not(target_os = "qurt"))] use crate::os::unix::prelude::*; use crate::sync::{PoisonError, RwLock}; use crate::sys::cvt; @@ -138,9 +141,15 @@ pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { }) } +#[cfg(not(target_os = "qurt"))] pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> { run_with_cstr(n.as_bytes(), &|nbuf| { let _guard = ENV_LOCK.write(); cvt(unsafe { libc::unsetenv(nbuf.as_ptr()) }).map(drop) }) } + +#[cfg(target_os = "qurt")] +pub unsafe fn unsetenv(_n: &OsStr) -> io::Result<()> { + panic!("remove_var is not supported on this platform") +} diff --git a/library/std/src/sys/exit.rs b/library/std/src/sys/exit.rs index b9ebe2d974fec..52f5f297bc9e6 100644 --- a/library/std/src/sys/exit.rs +++ b/library/std/src/sys/exit.rs @@ -129,7 +129,7 @@ pub fn exit(code: i32) -> ! { } crate::intrinsics::abort() } - any(target_family = "unix", target_os = "wasi") => unsafe { + any(target_family = "unix", target_os = "qurt", target_os = "wasi") => unsafe { libc::exit(code as crate::ffi::c_int) }, target_os = "vexos" => { diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs index 02d61a62f4e6b..746a839f5b1e8 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -3,7 +3,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] cfg_select! { - any(target_family = "unix", target_os = "wasi") => { + any(target_family = "unix", target_os = "wasi", target_os = "qurt") => { mod unix; pub use unix::*; } diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index aa84f6bc28023..12473c438a910 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -8,6 +8,7 @@ mod tests; target_os = "l4re", target_os = "android", target_os = "hurd", + target_os = "qurt", )))] use libc::off_t as off64_t; #[cfg(any( @@ -46,6 +47,8 @@ cfg_select! { // #[cfg(gnu_file_offset_bits64)]. use libc::{pread64, pwrite64}; } + // QuRT lacks pread/pwrite; handled below. + target_os = "qurt" => {} _ => { use libc::{pread as pread64, pwrite as pwrite64}; } @@ -117,6 +120,7 @@ const fn max_iov() -> usize { target_os = "openbsd", target_os = "horizon", target_os = "vita", + target_os = "qurt", target_vendor = "apple", target_os = "cygwin", )))] @@ -144,6 +148,7 @@ impl FileDesc { #[cfg(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" )))] @@ -161,6 +166,7 @@ impl FileDesc { #[cfg(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" ))] @@ -173,6 +179,7 @@ impl FileDesc { cfg!(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx", target_os = "wasi", @@ -184,6 +191,7 @@ impl FileDesc { (&mut me).read_to_end(buf) } + #[cfg(not(target_os = "qurt"))] pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { cvt(unsafe { pread64( @@ -196,6 +204,11 @@ impl FileDesc { .map(|n| n as usize) } + #[cfg(target_os = "qurt")] + pub fn read_at(&self, _buf: &mut [u8], _offset: u64) -> io::Result { + Err(io::const_error!(io::ErrorKind::Unsupported, "pread not supported on QuRT")) + } + pub fn read_buf(&self, mut cursor: BorrowedCursor<'_, u8>) -> io::Result<()> { // SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes let ret = cvt(unsafe { @@ -213,6 +226,7 @@ impl FileDesc { Ok(()) } + #[cfg(not(target_os = "qurt"))] pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_, u8>, offset: u64) -> io::Result<()> { // SAFETY: `cursor.as_mut()` starts with `cursor.capacity()` writable bytes let ret = cvt(unsafe { @@ -231,6 +245,11 @@ impl FileDesc { Ok(()) } + #[cfg(target_os = "qurt")] + pub fn read_buf_at(&self, _cursor: BorrowedCursor<'_, u8>, _offset: u64) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "pread not supported on QuRT")) + } + #[cfg(any( target_os = "aix", target_os = "dragonfly", // DragonFly 1.5 @@ -380,6 +399,7 @@ impl FileDesc { #[cfg(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" )))] @@ -397,6 +417,7 @@ impl FileDesc { #[cfg(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" ))] @@ -409,12 +430,14 @@ impl FileDesc { cfg!(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx", target_os = "wasi", ))) } + #[cfg(not(target_os = "qurt"))] pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { unsafe { cvt(pwrite64( @@ -427,6 +450,11 @@ impl FileDesc { } } + #[cfg(target_os = "qurt")] + pub fn write_at(&self, _buf: &[u8], _offset: u64) -> io::Result { + Err(io::const_error!(io::ErrorKind::Unsupported, "pwrite not supported on QuRT")) + } + #[cfg(any( target_os = "aix", target_os = "dragonfly", // DragonFly 1.5 @@ -577,6 +605,10 @@ impl FileDesc { target_os = "nto", target_os = "qnx", target_os = "wasi", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "qurt", )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -613,9 +645,14 @@ impl FileDesc { Ok(()) } } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "qurt" + ))] pub fn set_cloexec(&self) -> io::Result<()> { - // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to, + // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS, Vita, and QuRT but there's no need to, // because none of them supports spawning processes. Ok(()) } diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index b2666eb2a3da9..9efe2f7825fc5 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -6,16 +6,16 @@ use crate::path::{Path, PathBuf}; pub mod common; cfg_select! { - any(target_family = "unix", target_os = "wasi") => { + any(target_family = "unix", target_os = "wasi", target_os = "qurt") => { mod unix; use unix as imp; #[cfg(any(target_os = "linux", target_os = "android"))] pub(super) use unix::CachedFileMetadata; - #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] + #[cfg(not(any(target_os = "fuchsia", target_os = "wasi", target_os = "qurt")))] pub use unix::chroot; - #[cfg(not(target_os = "wasi"))] + #[cfg(not(any(target_os = "wasi", target_os = "qurt")))] pub(crate) use unix::debug_assert_fd_is_open; - #[cfg(not(target_os = "wasi"))] + #[cfg(not(any(target_os = "wasi", target_os = "qurt")))] pub use unix::{chown, fchown, lchown, mkfifo}; use crate::sys::helpers::run_path_with_cstr as with_native_path; @@ -54,7 +54,12 @@ cfg_select! { } // FIXME: Replace this with platform-specific path conversion functions. -#[cfg(not(any(target_family = "unix", target_os = "windows", target_os = "wasi")))] +#[cfg(not(any( + target_family = "unix", + target_os = "windows", + target_os = "wasi", + target_os = "qurt" +)))] #[inline] pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> io::Result { f(path) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index b33ebadebe4ad..4927535cd0bca 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -32,11 +32,18 @@ use libc::{ target_os = "android", target_os = "hurd", target_os = "l4re", + target_os = "qurt", )))] use libc::{ dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, off_t as off64_t, open as open64, stat as stat64, }; +// QuRT doesn't have lstat - use stat instead +#[cfg(target_os = "qurt")] +use libc::{ + dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, + off_t as off64_t, open as open64, stat as stat64, stat as lstat64, +}; #[cfg(target_os = "l4re")] use libc::{ dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64, lstat as lstat64, @@ -49,7 +56,9 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt::{self, Write as _}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +#[cfg(target_os = "qurt")] +use crate::os::qurt::prelude::*; #[cfg(target_family = "unix")] use crate::os::unix::prelude::*; #[cfg(target_os = "wasi")] @@ -65,6 +74,7 @@ use crate::sys::weak::syscall; #[cfg(target_os = "android")] use crate::sys::weak::weak; use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, cvt_r}; +#[cfg_attr(target_os = "qurt", allow(unused_imports))] use crate::{mem, ptr}; // Used by rustc for checking the definitions of other function with the same symbol names @@ -269,6 +279,7 @@ cfg_select! { target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nto", target_os = "qnx", @@ -414,6 +425,7 @@ struct dirent64_min { target_os = "aix", target_os = "nto", target_os = "qnx", + target_os = "qurt", target_os = "vita", )))] d_type: u8, @@ -572,6 +584,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "qurt", target_os = "rtems", target_os = "nuttx", )))] @@ -589,6 +602,7 @@ impl FileAttr { #[cfg(any( all(target_os = "vxworks", vxworks_lt_25_09), target_os = "espidf", + target_os = "qurt", target_os = "vita", target_os = "rtems", ))] @@ -612,6 +626,7 @@ impl FileAttr { target_os = "horizon", target_os = "vita", target_os = "hurd", + target_os = "qurt", target_os = "rtems", target_os = "nuttx", )))] @@ -629,6 +644,7 @@ impl FileAttr { #[cfg(any( all(target_os = "vxworks", vxworks_lt_25_09), target_os = "espidf", + target_os = "qurt", target_os = "vita", target_os = "rtems" ))] @@ -939,6 +955,7 @@ impl Iterator for ReadDir { target_os = "nto", target_os = "qnx", target_os = "vita", + target_os = "qurt", )))] d_type: (*entry_ptr).d_type as u8, }; @@ -962,6 +979,7 @@ impl Iterator for ReadDir { /// So we check file flags instead which live on the file descriptor and not the underlying file. /// The downside is that it costs an extra syscall, so we only do it for debug. #[inline] +#[cfg_attr(target_os = "qurt", allow(dead_code))] pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { use crate::sys::io::errno; @@ -981,6 +999,7 @@ impl Drop for DirStream { target_os = "redox", target_os = "nto", target_os = "qnx", + target_os = "qurt", target_os = "vita", target_os = "hurd", target_os = "espidf", @@ -1070,6 +1089,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "qnx", + target_os = "qurt", target_os = "vita", target_os = "l4re", ))] @@ -1085,6 +1105,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "qnx", + target_os = "qurt", target_os = "vita", target_os = "l4re", )))] @@ -1263,6 +1284,7 @@ impl File { Ok(FileAttr::from_stat64(stat)) } + #[cfg(not(target_os = "qurt"))] pub fn fsync(&self) -> io::Result<()> { cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); @@ -1277,6 +1299,12 @@ impl File { } } + #[cfg(target_os = "qurt")] + pub fn fsync(&self) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "fsync not supported on QuRT")) + } + + #[cfg(not(target_os = "qurt"))] pub fn datasync(&self) -> io::Result<()> { cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); @@ -1320,6 +1348,11 @@ impl File { } } + #[cfg(target_os = "qurt")] + pub fn datasync(&self) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "datasync not supported on QuRT")) + } + pub fn lock(&self) -> io::Result<()> { cfg_select! { any( @@ -1544,11 +1577,17 @@ impl File { self.0.duplicate().map(File) } + #[cfg(not(target_os = "qurt"))] pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; Ok(()) } + #[cfg(target_os = "qurt")] + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "fchmod not supported on QuRT")) + } + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { cfg_select! { any( @@ -1556,10 +1595,11 @@ impl File { target_os = "espidf", target_os = "horizon", target_os = "nuttx", - target_os = "l4re" + target_os = "l4re", + target_os = "qurt" ) => { // Redox doesn't appear to support `UTIME_OMIT`. - // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore + // ESP-IDF, HorizonOS, and QuRT do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. let _ = times; Err(io::const_error!( @@ -1644,6 +1684,7 @@ impl File { target_os = "espidf", target_os = "horizon", target_os = "nuttx", + target_os = "qurt", )))] fn file_time_to_timespec(time: Option) -> io::Result { match time { @@ -1880,10 +1921,22 @@ pub fn rename(old: &CStr, new: &CStr) -> io::Result<()> { cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ()) } +#[cfg(not(target_os = "qurt"))] pub fn set_perm(p: &CStr, perm: FilePermissions) -> io::Result<()> { cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()) } +#[cfg(target_os = "qurt")] +pub fn set_perm(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "chmod not supported on QuRT")) +} + +#[cfg(target_os = "qurt")] +pub fn set_perm_nofollow(_p: &CStr, _perm: FilePermissions) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "fchmodat not supported on QuRT")) +} + +#[cfg(not(target_os = "qurt"))] pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. // Their filesystems do not have symbolic links, so no special handling is required. @@ -1916,6 +1969,7 @@ pub fn rmdir(p: &CStr) -> io::Result<()> { cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()) } +#[cfg(not(target_os = "qurt"))] pub fn readlink(c_path: &CStr) -> io::Result { let p = c_path.as_ptr(); @@ -1942,12 +1996,27 @@ pub fn readlink(c_path: &CStr) -> io::Result { } } +#[cfg(target_os = "qurt")] +pub fn readlink(_c_path: &CStr) -> io::Result { + Err(io::const_error!(io::ErrorKind::Unsupported, "readlink not supported on QuRT")) +} + +#[cfg(not(target_os = "qurt"))] pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> { cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ()) } +#[cfg(target_os = "qurt")] +pub fn symlink(_original: &CStr, _link: &CStr) -> io::Result<()> { + Err(io::const_error!(io::ErrorKind::Unsupported, "symlink not supported on QuRT")) +} + pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { cfg_select! { + target_os = "qurt" => { + let _ = (original, link); + Err(io::const_error!(io::ErrorKind::Unsupported, "link not supported on QuRT")) + } any( // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. // POSIX leaves it implementation-defined whether `link` follows @@ -1963,6 +2032,7 @@ pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { target_env = "nto70", ) => { cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; + Ok(()) } _ => { // Where we can, use `linkat` instead of `link`; see the comment above @@ -1970,9 +2040,9 @@ pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?; + Ok(()) } } - Ok(()) } pub fn stat(p: &CStr) -> io::Result { @@ -2009,6 +2079,7 @@ pub fn lstat(p: &CStr) -> io::Result { Ok(FileAttr::from_stat64(stat)) } +#[cfg(not(target_os = "qurt"))] pub fn canonicalize(path: &CStr) -> io::Result { let r = unsafe { libc::realpath(path.as_ptr(), ptr::null_mut()) }; if r.is_null() { @@ -2021,6 +2092,11 @@ pub fn canonicalize(path: &CStr) -> io::Result { }))) } +#[cfg(target_os = "qurt")] +pub fn canonicalize(_path: &CStr) -> io::Result { + Err(io::const_error!(io::ErrorKind::Unsupported, "realpath not supported on QuRT")) +} + fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::File; use crate::sys::fs::common::NOT_FILE_ERROR; @@ -2041,7 +2117,8 @@ fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Resu target_os = "horizon", target_os = "nuttx", target_os = "vita", - target_os = "rtems" + target_os = "rtems", + target_os = "qurt" ) => { let _ = (p, times, follow_symlinks); Err(io::const_error!(io::ErrorKind::Unsupported, "setting file times not supported")) @@ -2138,7 +2215,7 @@ pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { set_times_impl(p, times, false) } -#[cfg(any(target_os = "espidf", target_os = "wasi"))] +#[cfg(any(target_os = "espidf", target_os = "qurt", target_os = "wasi"))] fn open_to_and_set_permissions( to: &Path, _reader_metadata: &crate::fs::Metadata, @@ -2149,7 +2226,7 @@ fn open_to_and_set_permissions( Ok((writer, writer_metadata)) } -#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] +#[cfg(not(any(target_os = "espidf", target_os = "qurt", target_os = "wasi")))] fn open_to_and_set_permissions( to: &Path, reader_metadata: &crate::fs::Metadata, @@ -2297,7 +2374,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { Ok(bytes_copied as u64) } -#[cfg(not(target_os = "wasi"))] +#[cfg(not(any(target_os = "wasi", target_os = "qurt")))] pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -2305,13 +2382,13 @@ pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { }) } -#[cfg(not(target_os = "wasi"))] +#[cfg(not(any(target_os = "wasi", target_os = "qurt")))] pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?; Ok(()) } -#[cfg(not(any(target_os = "vxworks", target_os = "wasi")))] +#[cfg(not(any(target_os = "vxworks", target_os = "wasi", target_os = "qurt")))] pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -2320,23 +2397,26 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { } #[cfg(target_os = "vxworks")] -pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { - let (_, _, _) = (path, uid, gid); +pub fn lchown(_path: &Path, _uid: u32, _gid: u32) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) } -#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks", target_os = "wasi")))] +#[cfg(not(any( + target_os = "fuchsia", + target_os = "vxworks", + target_os = "wasi", + target_os = "qurt" +)))] pub fn chroot(dir: &Path) -> io::Result<()> { run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) } #[cfg(target_os = "vxworks")] -pub fn chroot(dir: &Path) -> io::Result<()> { - let _ = dir; +pub fn chroot(_dir: &Path) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) } -#[cfg(not(target_os = "wasi"))] +#[cfg(not(any(target_os = "wasi", target_os = "qurt")))] pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ()) @@ -2345,11 +2425,12 @@ pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> { pub use remove_dir_impl::remove_dir_all; -// Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri +// Fallback for REDOX, ESP-ID, Horizon, QuRT, Vita, Vxworks and Miri #[cfg(any( target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nto", target_os = "qnx", @@ -2366,6 +2447,7 @@ mod remove_dir_impl { target_os = "redox", target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nto", target_os = "qnx", diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index a56030f17709f..42012e0a3473e 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -19,7 +19,7 @@ cfg_select! { mod uefi; pub use uefi::*; } - any(target_family = "unix", target_os = "wasi", target_os = "teeos") => { + any(target_family = "unix", target_os = "wasi", target_os = "teeos", target_os = "qurt",) => { mod unix; pub use unix::*; } diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs index 12acde7311e4c..ea24bcd1e3f1f 100644 --- a/library/std/src/sys/io/error/unix.rs +++ b/library/std/src/sys/io/error/unix.rs @@ -39,6 +39,7 @@ unsafe extern "C" { #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] #[cfg_attr(target_os = "aix", link_name = "_Errno")] + #[cfg_attr(target_os = "qurt", link_name = "_Geterrno")] // SAFETY: this will always return the same pointer on a given thread. #[unsafe(ffi_const)] pub safe fn errno_location() -> *mut c_int; diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 33182e4eb5539..1c1ca64cffc9f 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -4,7 +4,7 @@ mod error; mod is_terminal { cfg_select! { - any(target_family = "unix", target_os = "wasi") => { + any(target_family = "unix", target_os = "wasi", target_os = "qurt") => { mod isatty; pub use isatty::*; } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index 7a4d3a97b8324..41e747a07ca70 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -1,7 +1,10 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; +#[cfg(not(target_os = "qurt"))] use crate::sys::unsupported; +#[cfg(target_os = "qurt")] +use crate::sys::unsupported::unsupported; use crate::time::Duration; pub struct TcpStream(!); diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 88d9d42059900..3371dcc87e44c 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -4,7 +4,7 @@ #![allow(missing_debug_implementations)] cfg_select! { - unix => { + any(unix, target_os = "qurt") => { mod unix; pub use self::unix::*; } diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index e4a18794f7823..6ba4a90e8bff7 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -58,6 +58,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::open64 as open; + #[cfg(not(target_os = "qurt"))] if opened_devnull != -1 { if libc::dup(opened_devnull) != -1 { return; @@ -83,6 +84,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "horizon", target_os = "vita", target_os = "rtems", + target_os = "qurt", // The poll on Darwin doesn't set POLLNVAL for closed fds when `events == 0`. target_vendor = "apple", )))] @@ -127,6 +129,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "l4re", target_os = "horizon", target_os = "vita", + target_os = "qurt", )))] { use crate::sys::io::errno; @@ -205,6 +208,7 @@ static ON_BROKEN_PIPE_USED: crate::sync::atomic::Atomic = target_os = "vxworks", target_os = "vita", target_os = "nuttx", + target_os = "qurt", )))] pub(crate) fn on_broken_pipe_used() -> bool { ON_BROKEN_PIPE_USED.load(crate::sync::atomic::Ordering::Relaxed) @@ -374,6 +378,7 @@ cfg_select! { target_os = "vita", target_os = "nuttx", target_os = "l4re", + target_os = "qurt", ))] pub fn unsupported() -> crate::io::Result { Err(unsupported_err()) @@ -385,6 +390,7 @@ pub fn unsupported() -> crate::io::Result { target_os = "vita", target_os = "nuttx", target_os = "l4re", + target_os = "qurt", ))] pub fn unsupported_err() -> crate::io::Error { io::Error::UNSUPPORTED_PLATFORM diff --git a/library/std/src/sys/paths/mod.rs b/library/std/src/sys/paths/mod.rs index 57f894249ae74..f1733e06246b2 100644 --- a/library/std/src/sys/paths/mod.rs +++ b/library/std/src/sys/paths/mod.rs @@ -37,7 +37,7 @@ cfg_select! { mod uefi; use uefi as imp; } - target_family = "unix" => { + any(target_family = "unix", target_os = "qurt") => { mod unix; use unix as imp; } diff --git a/library/std/src/sys/paths/unix.rs b/library/std/src/sys/paths/unix.rs index e023451b13c8c..f0f43c7cf6627 100644 --- a/library/std/src/sys/paths/unix.rs +++ b/library/std/src/sys/paths/unix.rs @@ -5,6 +5,9 @@ use libc::{c_char, c_int, c_void}; use crate::ffi::{CStr, OsStr, OsString}; +#[cfg(target_os = "qurt")] +use crate::os::qurt::prelude::*; +#[cfg(not(target_os = "qurt"))] use crate::os::unix::prelude::*; use crate::path::{self, PathBuf}; use crate::sys::helpers::run_path_with_cstr; @@ -45,12 +48,12 @@ pub fn getcwd() -> io::Result { } } -#[cfg(target_os = "espidf")] +#[cfg(any(target_os = "espidf", target_os = "qurt"))] pub fn chdir(_p: &path::Path) -> io::Result<()> { crate::sys::pal::unsupported() } -#[cfg(not(target_os = "espidf"))] +#[cfg(not(any(target_os = "espidf", target_os = "qurt")))] pub fn chdir(p: &path::Path) -> io::Result<()> { let result = run_path_with_cstr(p, &|p| unsafe { Ok(libc::chdir(p.as_ptr())) })?; if result == 0 { Ok(()) } else { Err(io::Error::last_os_error()) } @@ -383,7 +386,7 @@ pub fn current_exe() -> io::Result { path.canonicalize() } -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "qurt"))] pub fn current_exe() -> io::Result { crate::sys::pal::unsupported() } @@ -442,6 +445,7 @@ pub fn home_dir() -> Option { target_os = "horizon", target_os = "vita", target_os = "nuttx", + target_os = "qurt", all(target_vendor = "apple", not(target_os = "macos")), ))] unsafe fn fallback() -> Option { @@ -456,6 +460,7 @@ pub fn home_dir() -> Option { target_os = "horizon", target_os = "vita", target_os = "nuttx", + target_os = "qurt", all(target_vendor = "apple", not(target_os = "macos")), )))] unsafe fn fallback() -> Option { diff --git a/library/std/src/sys/pipe/unsupported.rs b/library/std/src/sys/pipe/unsupported.rs index 3b1a71eb3af17..fba7f190c6c34 100644 --- a/library/std/src/sys/pipe/unsupported.rs +++ b/library/std/src/sys/pipe/unsupported.rs @@ -56,7 +56,7 @@ impl fmt::Debug for Pipe { } } -#[cfg(any(unix, target_os = "hermit", target_os = "wasi"))] +#[cfg(any(unix, target_os = "hermit", target_os = "qurt", target_os = "wasi"))] mod unix_traits { use super::Pipe; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index f46870e0c4042..ecae7a20bd948 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -46,7 +46,7 @@ pub use imp::{ target_os = "horizon", target_os = "vita", target_os = "nuttx", - target_os = "l4re" + target_os = "l4re", )) ), target_os = "windows", @@ -85,7 +85,7 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< target_os = "horizon", target_os = "vita", target_os = "nuttx", - target_os = "l4re" + target_os = "l4re", )) ), target_os = "windows", diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index f7d7f489ec079..a30d7bea4c418 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -5,7 +5,10 @@ use crate::num::NonZero; use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; +#[cfg(not(target_os = "qurt"))] use crate::sys::unsupported; +#[cfg(target_os = "qurt")] +use crate::sys::unsupported::unsupported; use crate::{fmt, io}; //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index bc3dd501730b7..f973870c42b33 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -102,6 +102,7 @@ cfg_select! { target_os = "xous", target_os = "vexos", target_os = "l4re", + target_os = "qurt", ) => { // FIXME: finally remove std support for wasm32-unknown-unknown // FIXME: add random data generation to xous @@ -119,6 +120,7 @@ cfg_select! { target_os = "xous", target_os = "vexos", target_os = "l4re", + target_os = "qurt", )))] pub fn hashmap_random_keys() -> (u64, u64) { let mut buf = [0; 16]; diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs index 86d0f3fe49cb3..93bb680fedf72 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -1,7 +1,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] cfg_select! { - any(target_family = "unix", target_os = "hermit", target_os = "wasi") => { + any(target_family = "unix", target_os = "hermit", target_os = "wasi", target_os = "qurt",) => { mod unix; pub use unix::*; } diff --git a/library/std/src/sys/stdio/unix.rs b/library/std/src/sys/stdio/unix.rs index 205478f2a5e99..fc9fd39d6b1e2 100644 --- a/library/std/src/sys/stdio/unix.rs +++ b/library/std/src/sys/stdio/unix.rs @@ -1,6 +1,6 @@ #[cfg(target_os = "hermit")] use hermit_abi::{EBADF, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; -#[cfg(any(target_family = "unix", target_os = "wasi"))] +#[cfg(any(target_family = "unix", target_os = "wasi", target_os = "qurt"))] use libc::{EBADF, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index 615781ea9b7dc..15206e8b58f3e 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -15,7 +15,7 @@ cfg_select! { mod futex; pub use futex::Condvar; } - any(target_family = "unix", target_os = "teeos") => { + any(target_family = "unix", target_os = "teeos", target_os = "qurt") => { mod pthread; pub use pthread::Condvar; } diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index 895ab9c895697..38d7ec3451251 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -18,7 +18,7 @@ cfg_select! { mod fuchsia; pub use fuchsia::Mutex; } - any(target_family = "unix", target_os = "teeos") => { + any(target_family = "unix", target_os = "teeos", target_os = "qurt") => { mod pthread; pub use pthread::Mutex; } diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index eee7edf8575fc..8572275c408fe 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -27,6 +27,7 @@ cfg_select! { any( windows, target_family = "unix", + target_os = "qurt", all(target_vendor = "fortanix", target_env = "sgx"), target_os = "solid_asp3", target_os = "xous", diff --git a/library/std/src/sys/sync/rwlock/mod.rs b/library/std/src/sys/sync/rwlock/mod.rs index 9991f290e46d1..dd3b4ee074a4f 100644 --- a/library/std/src/sys/sync/rwlock/mod.rs +++ b/library/std/src/sys/sync/rwlock/mod.rs @@ -21,6 +21,7 @@ cfg_select! { all(target_vendor = "fortanix", target_env = "sgx"), target_os = "xous", target_os = "teeos", + target_os = "qurt", ) => { mod queue; pub use queue::RwLock; diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index f1385ef7bdde6..1ff6be3cb5852 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -36,7 +36,7 @@ cfg_select! { mod xous; pub use xous::Parker; } - any(target_family = "unix", target_os = "teeos") => { + any(target_family = "unix", target_os = "teeos", target_os = "qurt") => { mod pthread; pub use pthread::Parker; } diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs index fb5d65150395d..fcdf1b0e9fd2f 100644 --- a/library/std/src/sys/thread/mod.rs +++ b/library/std/src/sys/thread/mod.rs @@ -48,7 +48,7 @@ cfg_select! { mod unsupported; pub use unsupported::{DEFAULT_MIN_STACK_SIZE, Thread, current_os_id, set_name, yield_now}; } - any(target_family = "unix", target_os = "wasi") => { + any(target_family = "unix", target_os = "wasi", target_os = "qurt") => { mod unix; #[cfg(not(any( target_env = "newlib", @@ -58,6 +58,7 @@ cfg_select! { target_os = "hurd", target_os = "aix", target_os = "wasi", + target_os = "qurt", )))] pub use unix::set_name; #[cfg(any( @@ -87,6 +88,7 @@ cfg_select! { target_os = "hurd", target_os = "aix", target_os = "wasi", + target_os = "qurt", ))] pub use unsupported::set_name; } diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index abcdfe89476e1..cc0eb05402d2f 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -441,7 +441,7 @@ pub fn set_name(name: &CStr) { target_os = "freebsd", target_os = "dragonfly", target_os = "nuttx", - target_os = "cygwin" + target_os = "cygwin", ))] pub fn set_name(name: &CStr) { unsafe { @@ -797,8 +797,13 @@ pub fn sleep_until(deadline: crate::time::Instant) { } pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); + #[cfg(not(target_os = "qurt"))] + { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); + } + #[cfg(target_os = "qurt")] + sleep(Duration::ZERO); } #[cfg(any(target_os = "android", target_os = "linux"))] diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 809ef5cfe9c60..b5bff8f91106a 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -151,7 +151,11 @@ pub(crate) mod guard { pub(crate) mod key { cfg_select! { any( - all(not(target_vendor = "apple"), not(target_family = "wasm"), target_family = "unix"), + all( + not(target_vendor = "apple"), + not(target_family = "wasm"), + any(target_family = "unix", target_os = "qurt") + ), all(not(target_thread_local), target_vendor = "apple"), target_os = "teeos", all(target_os = "wasi", target_env = "p3"), diff --git a/library/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs index 179c968ee2681..031006bfceb78 100644 --- a/library/std/src/sys/time/mod.rs +++ b/library/std/src/sys/time/mod.rs @@ -14,7 +14,13 @@ cfg_select! { mod uefi; use uefi as imp; } - any(target_os = "hermit", target_os = "teeos", target_family = "unix", target_os = "wasi") => { + any( + target_os = "hermit", + target_os = "teeos", + target_family = "unix", + target_os = "qurt", + target_os = "wasi" + ) => { mod unix; use unix as imp; } diff --git a/library/sysroot/src/lib.rs b/library/sysroot/src/lib.rs index 71ceb580a40c3..8dcc1a554e12c 100644 --- a/library/sysroot/src/lib.rs +++ b/library/sysroot/src/lib.rs @@ -1 +1,2 @@ +#![cfg_attr(target_os = "qurt", feature(restricted_std))] // This is intentionally empty since this crate is only used to depend on other library crates. diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index e4280520bd8ba..8f0a06e87cc9a 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -26,6 +26,7 @@ #![feature(panic_can_unwind)] #![cfg_attr(test, feature(test))] #![feature(thread_spawn_hook)] +#![cfg_attr(target_os = "qurt", feature(restricted_std))] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] #![warn(unreachable_pub)] diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 67abbe4faf2a1..6738333d2eaf0 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -350,6 +350,7 @@ impl Cargo { && !target.contains("cygwin") && !target.contains("aix") && !target.contains("xous") + && !target.contains("qurt") { self.rustflags.arg("-Clink-args=-Wl,-z,origin"); Some(format!("-Wl,-rpath,$ORIGIN/../{libdir}")) From f36d7c1efd40ad6533bc756baa070036c870975f Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Tue, 5 May 2026 15:51:22 -0700 Subject: [PATCH 2/3] Update hexagon-unknown-qurt platform support documentation Update documentation with details about how to leverage qurt, now that there's some level of libstd support --- .../platform-support/hexagon-unknown-qurt.md | 374 +++++++++++++----- 1 file changed, 286 insertions(+), 88 deletions(-) diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md index d33a90bf188c7..1920d12136452 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md @@ -15,33 +15,40 @@ Rust for Hexagon QuRT (Qualcomm Real-Time OS). ## Requirements This target is cross-compiled. There is support for `std`. The target uses -QuRT's standard library and runtime. +QuRT's POSIX-like threading and Dinkumware C library. By default, code generated with this target should run on Hexagon DSP hardware -running the QuRT real-time operating system. +running the QuRT real-time operating system, or on `qemu-system-hexagon`. - `-Ctarget-cpu=hexagonv69` targets Hexagon V69 architecture (default) - `-Ctarget-cpu=hexagonv73` adds support for instructions defined up to Hexagon V73 Functions marked `extern "C"` use the [Hexagon architecture calling convention](https://lists.llvm.org/pipermail/llvm-dev/attachments/20190916/21516a52/attachment-0001.pdf). -This target generates position-independent ELF binaries by default, making it -suitable for both static images and dynamic shared objects. - The [Hexagon SDK](https://softwarecenter.qualcomm.com/catalog/item/Hexagon_SDK) is -required for building programs for this target. +required for building and running programs for this target. It provides: -## Linking +- `hexagon-clang` (compiler and linker) +- QuRT runtime libraries (`libqurt.a`, `libposix.a`, etc.) +- CRT startup objects (`crt1.o`, `crt0.o`, `init.o`, `fini.o`, `debugmon.o`) +- `qemu-system-hexagon` emulator -This target uses `hexagon-clang` from the Hexagon SDK as the default linker. -The linker is available at paths like -`/opt/Hexagon_SDK/6.4.0.2/tools/HEXAGON_Tools/19.0.04/Tools/bin/hexagon-clang`. +Programs require the `restricted_std` feature gate: -Alternative linkers include: -- [eld](https://github.com/qualcomm/eld), which is provided with both - [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon) - and the Hexagon SDK -- `rust-lld` can be used by specifying `-C linker=rust-lld` +```rust +#![feature(restricted_std)] +``` + +## SDK setup + +Source the SDK environment script to set `HEXAGON_SDK_ROOT` and tool paths: + +```sh +source /opt/Hexagon_SDK/6.4.0.2/setup_sdk_env.source +``` + +This exports `HEXAGON_SDK_ROOT`, `DEFAULT_HEXAGON_TOOLS_ROOT`, and +`DEFAULT_QURT_PATH`. All paths below reference these variables. ## Building the target @@ -72,114 +79,305 @@ this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using `build-std` or similar. -## Static Image Targeting +## Linking + +QuRT executables require specific CRT startup objects and system libraries. +A `build.rs` script can derive the library paths from environment variables set +by the SDK's `setup_sdk_env.source`. -For static executables that run directly on QuRT, use the default target -configuration with additional linker flags: +### Example `build.rs` + +```rust +use std::env; +use std::path::PathBuf; + +fn main() { + // Only apply QuRT link configuration when targeting hexagon-unknown-qurt + let target = env::var("TARGET").unwrap_or_default(); + if target != "hexagon-unknown-qurt" { + return; + } + + // Derive paths from Hexagon SDK environment variables. + // These are set by: source $HEXAGON_SDK_ROOT/setup_sdk_env.source + let sdk = env::var("HEXAGON_SDK_ROOT") + .expect("HEXAGON_SDK_ROOT not set — source setup_sdk_env.source"); + let tools_root = env::var("DEFAULT_HEXAGON_TOOLS_ROOT") + .unwrap_or_else(|_| format!("{sdk}/tools/HEXAGON_Tools/19.0.04")); + let qurt_path = env::var("DEFAULT_QURT_PATH") + .unwrap_or_else(|_| format!("{sdk}/rtos/qurt")); + + let arch = "v69"; // match -Ctarget-cpu + let hexlib = PathBuf::from(&tools_root) + .join("Tools/target/hexagon/lib").join(arch).join("G0"); + let qurtlib = PathBuf::from(&qurt_path) + .join(format!("compute{arch}")).join("lib"); + + // CRT startup objects + println!("cargo:rustc-link-arg={}", qurtlib.join("crt1.o").display()); + println!("cargo:rustc-link-arg={}", hexlib.join("crt0.o").display()); + println!("cargo:rustc-link-arg={}", hexlib.join("init.o").display()); + println!("cargo:rustc-link-arg={}", qurtlib.join("debugmon.o").display()); + + // QuRT's ELF loader requires the program's load address to fall within + // the virtual memory pool (starting at page 0x40 = address 0x40000). + println!("cargo:rustc-link-arg=-Wl,--section-start=.start=0x40000"); + + // Stub symbols not available on QuRT + for sym in ["_Unwind_Backtrace", "_Unwind_GetIPInfo"] { + println!("cargo:rustc-link-arg=-Wl,--defsym={sym}=abort"); + } + + // Library search paths + println!("cargo:rustc-link-search=native={}", qurtlib.display()); + println!("cargo:rustc-link-search=native={}", hexlib.display()); + + // QuRT system libraries (use --start-group for circular deps) + println!("cargo:rustc-link-arg=-Wl,--start-group"); + for lib in ["qurt", "posix", "qurtcfs", "timer_main", "timer_island"] { + println!("cargo:rustc-link-lib=static={lib}"); + } + + // Exception handling and C runtime + println!("cargo:rustc-link-lib=static=c_eh"); + println!("cargo:rustc-link-lib=static=c"); + println!("cargo:rustc-link-lib=static=qcc"); + println!("cargo:rustc-link-arg=-Wl,--end-group"); + + // CRT finalization + println!("cargo:rustc-link-arg={}", hexlib.join("fini.o").display()); + + // Re-run if SDK path changes + println!("cargo:rerun-if-env-changed=HEXAGON_SDK_ROOT"); + println!("cargo:rerun-if-env-changed=DEFAULT_HEXAGON_TOOLS_ROOT"); + println!("cargo:rerun-if-env-changed=DEFAULT_QURT_PATH"); +} +``` + +### Compiling and linking + +With the `build.rs` above, build with: ```sh -# Build a static executable for QuRT -cargo rustc --target hexagon-unknown-qurt -- \ - -C link-args="-static -nostdlib" \ - -C link-args="-L/opt/Hexagon_SDK/6.3.0.0/rtos/qurt/computev69/lib" \ - -C link-args="-lqurt -lc" +source ${HEXAGON_SDK_ROOT}/setup_sdk_env.source + +cargo build --target hexagon-unknown-qurt \ + -Zbuild-std=core,alloc,std,panic_abort \ + -Zbuild-std-features=restricted-std ``` -This approach is suitable for: -- Standalone QuRT applications -- System-level services -- Boot-time initialization code -- Applications that need deterministic memory layout +Or with `rustc` directly, passing all link flags explicitly (set `HEXLIB` and +`QURTLIB` from the SDK environment as shown in the `build.rs` above): -## User-Loadable Shared Object Targeting +```sh +HEXLIB="${DEFAULT_HEXAGON_TOOLS_ROOT}/Tools/target/hexagon/lib/v69/G0" +QURTLIB="${DEFAULT_QURT_PATH}/computev69/lib" + +rustc program.rs \ + --target hexagon-unknown-qurt \ + --edition 2021 \ + -C linker=hexagon-clang \ + -C panic=abort \ + -C "link-args=-nostdlib" \ + -C "link-args=${QURTLIB}/crt1.o ${HEXLIB}/crt0.o ${HEXLIB}/init.o ${QURTLIB}/debugmon.o" \ + -C "link-args=-Wl,--section-start=.start=0x40000" \ + -C "link-args=-Wl,--defsym=_Unwind_Backtrace=abort" \ + -C "link-args=-Wl,--defsym=_Unwind_GetIPInfo=abort" \ + -C "link-args=-L${QURTLIB} -L${HEXLIB}" \ + -C "link-args=-Wl,--start-group" \ + -C "link-args=-lqurt -lposix -lqurtcfs -ltimer_main -ltimer_island" \ + -C "link-args=${HEXLIB}/libc_eh.a -lc -lqcc" \ + -C "link-args=-Wl,--end-group" \ + -C "link-args=${HEXLIB}/fini.o" \ + -o program +``` -For shared libraries that can be dynamically loaded by QuRT applications: +The above use hexagon-clang/ld.qcld, but an alternative linker is available: +- [eld](https://github.com/qualcomm/eld), which is provided with both + [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon) + and the Hexagon SDK + +## Testing + +Programs can be tested using `qemu-system-hexagon` from the Hexagon SDK. + +### Running a static executable on QEMU + +For programs linked as static executables (as shown in the linking examples +above), pass the program directly to `runelf.pbn`: ```sh -# Build a shared object for QuRT -cargo rustc --target hexagon-unknown-qurt \ - --crate-type=cdylib -- \ - -C link-args="-shared -fPIC" \ - -C link-args="-L/opt/Hexagon_SDK/6.3.0.0/rtos/qurt/computev69/lib" +${HEXAGON_SDK_ROOT}/tools/Tools/QEMUHexagon/bin/qemu-system-hexagon \ + -machine V69NA_1024 \ + -kernel ${DEFAULT_QURT_PATH}/computev69/sdksim_bin/runelf.pbn \ + -append "/path/to/program" ``` -This approach is suitable for: -- Plugin architectures -- Runtime-loadable modules -- Libraries shared between multiple applications -- Code that needs to be updated without system restart +The QuRT boot loader (`runelf.pbn`) is passed as `-kernel` and it loads the +user program specified via `-append`. No configuration files or cosim plugins +are needed — the machine model includes timer and interrupt controller +emulation. -## Configuration Options +### Running a shared object on QEMU -The target can be customized for different use cases: +The Hexagon SDK provides `run_main_on_hexagon_sim`, a QuRT program that +dynamically loads a user shared object and calls its `main()`. This is the +standard approach used by the SDK's build system for running tests. -### For Static Images -```toml -# In .cargo/config.toml -[target.hexagon-unknown-qurt] -rustflags = [ - "-C", "link-args=-static", - "-C", "link-args=-nostdlib", - "-C", "target-feature=-small-data" -] +First, build the Rust program as a shared object: + +```sh +rustc program.rs \ + --target hexagon-unknown-qurt \ + --edition 2021 \ + --crate-type cdylib \ + -C linker=hexagon-clang \ + -C panic=abort \ + -o libprogram.so ``` -### For Shared Objects -```toml -# In .cargo/config.toml -[target.hexagon-unknown-qurt] -rustflags = [ - "-C", "link-args=-shared", - "-C", "link-args=-fPIC", - "-C", "relocation-model=pic" -] +Then run it using `run_main_on_hexagon_sim`: + +```sh +RUN_MAIN="${HEXAGON_SDK_ROOT}/libs/run_main_on_hexagon/ship/hexagon_toolv19_v69/run_main_on_hexagon_sim" + +${HEXAGON_SDK_ROOT}/tools/Tools/QEMUHexagon/bin/qemu-system-hexagon \ + -machine V69NA_1024 \ + -kernel ${DEFAULT_QURT_PATH}/computev69/sdksim_bin/runelf.pbn \ + -append "${RUN_MAIN} -- libprogram.so" ``` -## Testing +Arguments after the `.so` filename are passed as `argc`/`argv` to `main()`: + +```sh + -append "${RUN_MAIN} -- libprogram.so arg1 arg2" +``` + +The `run_main_on_hexagon_sim` approach is useful for: +- Programs that need to be loaded dynamically (plugin architectures) +- Matching the SDK's standard test workflow +- Testing shared libraries built with `--crate-type cdylib` + +## Qualcomm Hexagon Libraries (QHL) + +The Hexagon SDK includes optimized math, BLAS, and DSP libraries that can be +called from Rust via `extern "C"` declarations: + +- **qhmath** — scalar and array math: `qhmath_sin_f`, `qhmath_cos_f`, + `qhmath_sqrt_f`, `qhmath_exp_f`, `qhmath_sigmoid_f`, etc. +- **qhblas** — BLAS operations: `qhblas_vector_add_af`, + `qhblas_f_vector_dot_af`, `qhblas_vector_scaling_af`, etc. +- **qhblas_hvx** — HVX-accelerated BLAS: `qhblas_hvx_vector_add_af`, + `qhblas_hvx_f_vector_dot_af`, `qhblas_hvx_vector_hadamard_af`, etc. +- **qhmath_hvx** — HVX-accelerated math: `qhmath_hvx_sin_af`, + `qhmath_hvx_cos_af`, `qhmath_hvx_sqrt_af` +- **qhdsp** — signal processing: `qhdsp_crc32_poly`, FFT, FIR/IIR filters + +To link QHL libraries, add these paths and libraries (in `build.rs` or as +`-C link-args`): + +```rust,ignore (snippet-missing-imports-and-context) +// In build.rs, inside the hexagon-unknown-qurt block: +let qhl = PathBuf::from(&sdk).join("libs/qhl/prebuilt/hexagon_toolv19_v69"); +let qhl_hvx = PathBuf::from(&sdk).join("libs/qhl_hvx/prebuilt/hexagon_toolv19_v69"); +println!("cargo:rustc-link-search=native={}", qhl.display()); +println!("cargo:rustc-link-search=native={}", qhl_hvx.display()); +for lib in ["qhmath", "qhblas", "qhdsp", "qhcomplex", + "qhmath_hvx", "qhblas_hvx", "qhdsp_hvx"] { + println!("cargo:rustc-link-lib=static={lib}"); +} +``` -Since `hexagon-unknown-qurt` requires the QuRT runtime environment, testing requires -either: -- Hexagon hardware with QuRT -- `hexagon-sim` -- QEMU (`qemu-system-hexagon`) +Example Rust usage: + +```rust,ignore (requires-qhl-libraries-to-link) +extern "C" { + fn qhmath_sqrt_f(x: f32) -> f32; + fn qhblas_hvx_vector_add_af( + i1: *const f32, i2: *const f32, out: *mut f32, size: u32, + ) -> i32; +} + +unsafe { + let sqrt4 = qhmath_sqrt_f(4.0); // 2.0 + let a = [1.0f32, 2.0, 3.0, 4.0]; + let b = [10.0f32, 20.0, 30.0, 40.0]; + let mut c = [0.0f32; 4]; + qhblas_hvx_vector_add_af(a.as_ptr(), b.as_ptr(), c.as_mut_ptr(), 4); + // c ≈ [11.0, 22.0, 33.0, 44.0] (HVX float has ~1e-5 precision) +} +``` + +## Working `std` functionality + +The following `std` features are expected to work: + +- **Heap allocation**: `Vec`, `String`, `Box`, `HashMap`, `BTreeMap`, `VecDeque`, + `Rc`, `Arc` +- **Formatting/IO**: `println!`, `eprintln!`, `format!`, `write!`, + `stdout().write_all()`, `stderr().write_all()` +- **Synchronization**: `Mutex`, `RwLock`, `Condvar`, `Once`, `OnceLock`, + `AtomicI32`, `AtomicU32`, `AtomicBool` (max atomic width is 32 bits) +- **Threading**: `thread::spawn`, `thread::Builder` (set stack size), + `thread::sleep`, `thread_local!`, `thread::current().id()` +- **Time**: `Instant::now()`, `SystemTime::now()`, `Duration` arithmetic +- **File I/O**: `File::create`, `File::open`, `fs::remove_file` +- **Environment**: `env::current_dir()`, `env::temp_dir()`, `env::var()` + (read-only) +- **Error handling**: `Result`, `Option` combinators +- **HVX SIMD**: `core::arch::hexagon` intrinsics (128-byte vectors via + `#![feature(stdarch_hexagon)]`) + +## Known limitations + +- **`panic=unwind` not functional at runtime**: The target compiles with + `panic=unwind` but panics abort instead of unwinding. Use `-C panic=abort`. +- **No process spawning**: `Command` / `process::exit` are not available +- **No networking**: Socket APIs are not supported +- **32-bit atomics maximum**: Use `AtomicU32`/`AtomicI32`, not + `AtomicU64`/`AtomicUsize` on this 32-bit target +- **Thread stack size**: QuRT's default heap is limited (~512 KB); use + `thread::Builder::new().stack_size(8192)` or similar small values to + avoid out-of-memory failures +- **Environment variables**: `env::set_var` is not functional; `env::var` works + for reading pre-set variables; `env::remove_var` panics +- **File I/O quirks**: QuRT's CFS (cosim filesystem) has known issues: + `write()` may report one extra byte written, `read()` may return 0 bytes + in the emulator, and `stat()` is not supported + +- **`_Unwind_Backtrace`**: Stubbed to `abort`; backtraces are not available ## Cross-compilation toolchains and C code -This target requires the proprietary [Hexagon SDK toolchain for C interoperability](https://softwarecenter.qualcomm.com/catalog/item/Hexagon_SDK): +This target requires the [Hexagon SDK](https://softwarecenter.qualcomm.com/catalog/item/Hexagon_SDK) +for C interoperability: -- **Sample SDK Path**: `/opt/Hexagon_SDK/6.3.0.0/` -- **Toolchain**: Use `hexagon-clang` from the Hexagon SDK -- **Libraries**: Link against QuRT system libraries as needed +- **Compiler**: `hexagon-clang` / `hexagon-clang++` +- **QuRT libraries**: `${DEFAULT_QURT_PATH}/computev69/lib/` +- **Hex tools libraries**: `${DEFAULT_HEXAGON_TOOLS_ROOT}/Tools/target/hexagon/lib/v69/G0/` +- **QHL libraries**: `${HEXAGON_SDK_ROOT}/libs/qhl/prebuilt/hexagon_toolv19_v69/` -### C Interoperability Example +### Simple C Interoperability Example ```rust -// lib.rs -#![no_std] -extern crate std; +#![feature(restricted_std)] #[unsafe(no_mangle)] -pub extern "C" fn rust_function() -> i32 { - // Your Rust code here - 42 +pub extern "C" fn rust_add(a: i32, b: i32) -> i32 { + a + b } fn main() { - // Example usage - let result = rust_function(); - assert_eq!(result, 42); + let result = rust_add(2, 3); + println!("result = {result}"); } ``` ```c -// wrapper.c -extern int rust_function(void); +// call_from_c.c +extern int rust_add(int a, int b); -int main() { - return rust_function(); +int use_rust(void) { + return rust_add(2, 3); } ``` - -The target supports both static linking for standalone applications and dynamic -linking for modular architectures, making it flexible for various QuRT -deployment scenarios. From 793d92a1b6dfda0cec6eb6036db97bf83c25cfa9 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Tue, 11 Aug 2026 17:49:21 -0500 Subject: [PATCH 3/3] std: opt hexagon-unknown-qurt out of unwinding The SDK's `libc_eh.a` unwinder does not define `_Unwind_GetIPInfo`, which `eh_personality` needs on every frame, so unwinding cannot work as it stands: the linker stub the docs previously recommended aborts on the first frame. Set `panic_strategy = Abort` and keep the target out of the personality and `libunwind` arms, as Hermit and UEFI do; `panic_unwind` already falls through to its aborting implementation. The `_Unwind_*` linker stubs are no longer needed, so drop them from the documented link flags. --- .../src/spec/targets/hexagon_unknown_qurt.rs | 7 ++++++- .../platform-support/hexagon-unknown-qurt.md | 18 +++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs index 82bf2b11073b3..2eed6df367ada 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs @@ -1,4 +1,6 @@ -use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Os, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{ + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn target() -> Target { let mut base = TargetOptions::default(); @@ -30,6 +32,9 @@ pub(crate) fn target() -> Target { dynamic_linking: true, executables: true, families: cvs![], + // The SDK's `libc_eh.a` unwinder lacks the `_Unwind_GetIPInfo` + // that `eh_personality` requires. + panic_strategy: PanicStrategy::Abort, has_thread_local: true, has_rpath: false, crt_static_default: false, diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md index 1920d12136452..c941f1e1e8ecc 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md @@ -123,11 +123,6 @@ fn main() { // the virtual memory pool (starting at page 0x40 = address 0x40000). println!("cargo:rustc-link-arg=-Wl,--section-start=.start=0x40000"); - // Stub symbols not available on QuRT - for sym in ["_Unwind_Backtrace", "_Unwind_GetIPInfo"] { - println!("cargo:rustc-link-arg=-Wl,--defsym={sym}=abort"); - } - // Library search paths println!("cargo:rustc-link-search=native={}", qurtlib.display()); println!("cargo:rustc-link-search=native={}", hexlib.display()); @@ -181,8 +176,6 @@ rustc program.rs \ -C "link-args=-nostdlib" \ -C "link-args=${QURTLIB}/crt1.o ${HEXLIB}/crt0.o ${HEXLIB}/init.o ${QURTLIB}/debugmon.o" \ -C "link-args=-Wl,--section-start=.start=0x40000" \ - -C "link-args=-Wl,--defsym=_Unwind_Backtrace=abort" \ - -C "link-args=-Wl,--defsym=_Unwind_GetIPInfo=abort" \ -C "link-args=-L${QURTLIB} -L${HEXLIB}" \ -C "link-args=-Wl,--start-group" \ -C "link-args=-lqurt -lposix -lqurtcfs -ltimer_main -ltimer_island" \ @@ -330,8 +323,11 @@ The following `std` features are expected to work: ## Known limitations -- **`panic=unwind` not functional at runtime**: The target compiles with - `panic=unwind` but panics abort instead of unwinding. Use `-C panic=abort`. +- **No unwinding**: The SDK ships a DWARF unwinder in `libc_eh.a`, but it + does not provide `_Unwind_GetIPInfo`, which Rust's `eh_personality` + requires (only the plain `_Unwind_GetIP` is available). The target + therefore sets `panic_strategy = "abort"`: panics abort the process and + `extern "C-unwind"` cannot propagate foreign exceptions. - **No process spawning**: `Command` / `process::exit` are not available - **No networking**: Socket APIs are not supported - **32-bit atomics maximum**: Use `AtomicU32`/`AtomicI32`, not @@ -344,8 +340,8 @@ The following `std` features are expected to work: - **File I/O quirks**: QuRT's CFS (cosim filesystem) has known issues: `write()` may report one extra byte written, `read()` may return 0 bytes in the emulator, and `stat()` is not supported - -- **`_Unwind_Backtrace`**: Stubbed to `abort`; backtraces are not available +- **Backtraces**: `std::backtrace` uses the no-op backend on this target, so + captured backtraces are empty ## Cross-compilation toolchains and C code