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..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(); @@ -29,7 +31,10 @@ pub(crate) fn target() -> Target { exe_suffix: ".elf".into(), dynamic_linking: true, executables: true, - families: cvs!["unix"], + 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/library/core/src/io/io_slice.rs b/library/core/src/io/io_slice.rs index 0bdd410d3e964..e4d8cbeec03f3 100644 --- a/library/core/src/io/io_slice.rs +++ b/library/core/src/io/io_slice.rs @@ -3,7 +3,7 @@ use crate::mem::take; use crate::ops::{Deref, DerefMut}; cfg_select! { - any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { + any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", 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/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 618e0dca7659b..aaa351b2a8ce1 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -218,7 +218,7 @@ fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> { all(target_arch = "wasm32", not(target_os = "emscripten")), target_os = "hermit", target_os = "trusty", - target_os = "motor" + target_os = "motor", )) => { cvt(unsafe {libc::dup2(other.as_raw_fd(), this.as_raw_fd())}).map(|_| ()) } diff --git a/library/std/src/process.rs b/library/std/src/process.rs index a398363cf4bf9..19ea0ec555b8d 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 73d35781f09bf..567acfb68ce67 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -66,6 +66,7 @@ unsafe fn realloc_fallback(ptr: *mut u8, old_layout: Layout, new_size: usize) -> cfg_select! { any( target_family = "unix", + target_os = "qurt", target_os = "wasi", target_os = "teeos", target_os = "trusty", 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 d0c67fbf6cbd7..35e4eb6ab888c 100644 --- a/library/std/src/sys/exit.rs +++ b/library/std/src/sys/exit.rs @@ -133,6 +133,7 @@ pub fn exit(code: i32) -> ! { } any( target_family = "unix", + target_os = "qurt", target_os = "wasi", ) => { unsafe { libc::exit(code as crate::ffi::c_int) } 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 d35a001bb6bab..e07653783addc 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( @@ -50,6 +51,8 @@ cfg_select! { use libc::pread64; use libc::pwrite64; } + // QuRT lacks pread/pwrite; handled below. + target_os = "qurt" => {} _ => { use libc::pread as pread64; use libc::pwrite as pwrite64; @@ -122,6 +125,7 @@ const fn max_iov() -> usize { target_os = "openbsd", target_os = "horizon", target_os = "vita", + target_os = "qurt", target_vendor = "apple", target_os = "cygwin", )))] @@ -149,6 +153,7 @@ impl FileDesc { #[cfg(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" )))] @@ -166,6 +171,7 @@ impl FileDesc { #[cfg(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" ))] @@ -178,6 +184,7 @@ impl FileDesc { cfg!(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx", target_os = "wasi", @@ -189,6 +196,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( @@ -201,6 +209,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 { @@ -218,6 +231,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 { @@ -236,6 +250,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 @@ -385,6 +404,7 @@ impl FileDesc { #[cfg(not(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" )))] @@ -402,6 +422,7 @@ impl FileDesc { #[cfg(any( target_os = "espidf", target_os = "horizon", + target_os = "qurt", target_os = "vita", target_os = "nuttx" ))] @@ -414,12 +435,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( @@ -432,6 +455,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 @@ -582,6 +610,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 { @@ -618,9 +650,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 b5a5fa93fd491..5028287c476ab 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -6,14 +6,14 @@ 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(not(target_os = "wasi"))] + #[cfg(not(any(target_os = "wasi", target_os = "qurt")))] pub use unix::{chown, fchown, lchown, mkfifo}; - #[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(any(target_os = "linux", target_os = "android"))] pub(super) use unix::CachedFileMetadata; @@ -52,7 +52,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 d34621083406a..dbb2d7ea40fc5 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" ))] @@ -937,6 +953,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, }; @@ -960,6 +977,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; @@ -979,6 +997,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", @@ -1068,6 +1087,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "qnx", + target_os = "qurt", target_os = "vita", target_os = "l4re", ))] @@ -1083,6 +1103,7 @@ impl DirEntry { target_os = "aix", target_os = "nto", target_os = "qnx", + target_os = "qurt", target_os = "vita", target_os = "l4re", )))] @@ -1261,6 +1282,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(()); @@ -1275,6 +1297,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(()); @@ -1318,6 +1346,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( @@ -1550,16 +1583,22 @@ 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(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "l4re") => { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", 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!( @@ -1626,6 +1665,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 { @@ -1862,10 +1902,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. @@ -1904,6 +1956,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(); @@ -1930,12 +1983,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 @@ -1951,14 +2019,15 @@ 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 // this one for details on why. 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 { @@ -1995,6 +2064,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() { @@ -2007,6 +2077,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; @@ -2021,7 +2096,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Result<()> { cfg_select! { - any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "vita", target_os = "rtems") => { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "vita", target_os = "rtems", target_os = "qurt") => { let _ = (p, times, follow_symlinks); Err(io::const_error!( io::ErrorKind::Unsupported, @@ -2100,7 +2175,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, @@ -2111,7 +2186,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, @@ -2259,7 +2334,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) }) @@ -2267,13 +2342,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) }) @@ -2282,23 +2357,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(|_| ()) @@ -2307,11 +2385,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", @@ -2328,6 +2407,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 1b6bfea27d3b0..d8650daa3f478 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -19,7 +19,12 @@ 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 5c51c5705a7aa..d98a4259c0158 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 f58c7f5cb95bc..bb5ad850218ef 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) @@ -370,6 +374,7 @@ cfg_select! { target_os = "vita", target_os = "nuttx", target_os = "l4re", + target_os = "qurt", ))] pub fn unsupported() -> crate::io::Result { Err(unsupported_err()) @@ -381,6 +386,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 8880a837af7f9..c20ba27cb6e7f 100644 --- a/library/std/src/sys/paths/mod.rs +++ b/library/std/src/sys/paths/mod.rs @@ -30,7 +30,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 114f0001b7faf..ec6e5c6e78466 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 5b0d19cc63eca..9b74e7671c631 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -107,6 +107,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 @@ -124,6 +125,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..915f206a4a77b 100644 --- a/library/std/src/sys/stdio/mod.rs +++ b/library/std/src/sys/stdio/mod.rs @@ -1,7 +1,12 @@ #![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 0e8a4ed9ece86..9ec06b5d32e84 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -18,6 +18,7 @@ cfg_select! { 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 9ec251b7e4049..f8969d917d3d3 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -21,6 +21,7 @@ cfg_select! { 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 3bdb6c9f85ebb..7966e31dc1f64 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 fe4c830ce38bb..bc3748c3c0d3a 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -39,6 +39,7 @@ cfg_select! { 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 9816981c7fc88..bd45fb1391234 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::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; } - any(target_family = "unix", target_os = "wasi") => { + any(target_family = "unix", target_os = "wasi", target_os = "qurt") => { mod unix; pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; #[cfg(not(any( @@ -59,6 +59,7 @@ cfg_select! { target_os = "hurd", target_os = "aix", target_os = "wasi", + target_os = "qurt", )))] pub use unix::set_name; #[cfg(any( @@ -85,6 +86,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 2dbb0314cb271..33a7438cbfdad 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -429,7 +429,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 { @@ -779,8 +779,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 cb954e475be1b..531bc71d1d840 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -147,7 +147,7 @@ pub(crate) mod key { all( not(target_vendor = "apple"), not(target_family = "wasm"), - target_family = "unix", + any(target_family = "unix", target_os = "qurt"), ), all(not(target_thread_local), target_vendor = "apple"), target_os = "teeos", diff --git a/library/std/src/sys/time/mod.rs b/library/std/src/sys/time/mod.rs index 6cd1850e7cca1..687667e580887 100644 --- a/library/std/src/sys/time/mod.rs +++ b/library/std/src/sys/time/mod.rs @@ -21,6 +21,7 @@ cfg_select! { any( target_os = "teeos", target_family = "unix", + target_os = "qurt", target_os = "wasi", ) => { mod unix; 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 3a1e4299b5475..764e3cc03df7d 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -333,6 +333,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}")) 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..c941f1e1e8ecc 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,301 @@ 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"); + + // 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=-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 + +- **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 + `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 +- **Backtraces**: `std::backtrace` uses the no-op backend on this target, so + captured backtraces are empty ## 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.