From 2034aa49d4e18c68bb18dc46c64573e88104d706 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 28 Oct 2025 13:21:37 +0800 Subject: [PATCH 1/3] xous: fix unwinding and unused symbol errors The unwinding feature is called `panic-unwind` and not `panic_unwind`. Adjust the feature gate to look for these values. Additionally, the `abort_internal()` call is no longer used. Signed-off-by: Sean Cross --- library/std/src/sys/pal/xous/mod.rs | 6 ------ library/std/src/sys/pal/xous/os.rs | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index e673157e0eb55..1208516652d38 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -1,7 +1,5 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::os::xous::ffi::exit; - pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -10,7 +8,3 @@ pub mod time; #[path = "../unsupported/common.rs"] mod common; pub use common::*; - -pub fn abort_internal() -> ! { - exit(101); -} diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index d9b8418e6c33c..2da711f89dfaf 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -11,7 +11,7 @@ pub(crate) mod params; static PARAMS_ADDRESS: Atomic<*mut u8> = AtomicPtr::new(core::ptr::null_mut()); #[cfg(not(test))] -#[cfg(feature = "panic_unwind")] +#[cfg(feature = "panic-unwind")] mod eh_unwinding { pub(crate) struct EhFrameFinder; pub(crate) static mut EH_FRAME_ADDRESS: usize = 0; @@ -45,7 +45,7 @@ mod c_compat { #[unsafe(no_mangle)] pub extern "C" fn _start(eh_frame: usize, params_address: usize) { - #[cfg(feature = "panic_unwind")] + #[cfg(feature = "panic-unwind")] { unsafe { super::eh_unwinding::EH_FRAME_ADDRESS = eh_frame }; unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok(); From 19264d567d6d243f80137c1483b28400b6300bca Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Mon, 1 Dec 2025 21:59:50 +0800 Subject: [PATCH 2/3] xous: fix calling of rust_main_thread_not_inlined This function was incorrectly called run_main_thread_not_inlined when invoked. Signed-off-by: Sean Cross --- library/std/src/sys/thread/xous.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/thread/xous.rs b/library/std/src/sys/thread/xous.rs index 6c2cdfa4acddf..208d43bb2c065 100644 --- a/library/std/src/sys/thread/xous.rs +++ b/library/std/src/sys/thread/xous.rs @@ -87,7 +87,7 @@ impl Thread { // dealloc calls from being reordered to after the TLS has been destroyed. // See https://github.com/rust-lang/rust/pull/144465#pullrequestreview-3289729950 // for more context. - run_main_thread_not_inlined(init); + rust_main_thread_not_inlined(init); // Destroy TLS, which will free the TLS page and call the destructor for // any thread local storage (if any). From f4a5bc0159e1fe0be551cb712344409f55349992 Mon Sep 17 00:00:00 2001 From: Sean Cross Date: Tue, 6 Aug 2024 22:45:31 +0800 Subject: [PATCH 3/3] std: xous: add support for path Add support for the Xous-specific path implementation. Signed-off-by: Sean Cross --- library/std/src/os/xous/mod.rs | 1 + library/std/src/os/xous/path.rs | 40 +++++ library/std/src/sys/path/mod.rs | 4 + library/std/src/sys/path/xous.rs | 248 +++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 library/std/src/os/xous/path.rs create mode 100644 library/std/src/sys/path/xous.rs diff --git a/library/std/src/os/xous/mod.rs b/library/std/src/os/xous/mod.rs index 4b21695c4ac7e..7c616dee085b9 100644 --- a/library/std/src/os/xous/mod.rs +++ b/library/std/src/os/xous/mod.rs @@ -7,6 +7,7 @@ pub mod ffi; #[stable(feature = "rust1", since = "1.0.0")] pub mod services; +pub mod path; /// A prelude for conveniently writing platform-specific code. /// /// Includes all extension traits, and some important type definitions. diff --git a/library/std/src/os/xous/path.rs b/library/std/src/os/xous/path.rs new file mode 100644 index 0000000000000..5a8212e6cd403 --- /dev/null +++ b/library/std/src/os/xous/path.rs @@ -0,0 +1,40 @@ +#![stable(feature = "rust1", since = "1.0.0")] + +use crate::path; + +#[stable(feature = "file_type_ext", since = "1.5.0")] +pub trait PathExt { + /// Returns `true` if this file type is a basis. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::os::xous::fs::FileTypeExt; + /// use std::io; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata(":.System")?; + /// let file_type = meta.file_type(); + /// assert!(file_type.is_basis()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "file_type_ext", since = "1.5.0")] + fn is_basis(&self) -> bool; +} + +#[stable(feature = "file_type_ext", since = "1.5.0")] +impl PathExt for path::Path { + fn is_basis(&self) -> bool { + let Some(as_str) = self.as_os_str().to_str() else { + return false; + }; + + let Ok(path) = crate::sys::path::split_basis_dict_key(as_str) else { + return false; + }; + + path.basis.is_some() && path.dict.is_none() && path.key.is_none() + } +} diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs index 254683bc83f91..eb95a918a0cbb 100644 --- a/library/std/src/sys/path/mod.rs +++ b/library/std/src/sys/path/mod.rs @@ -16,6 +16,10 @@ cfg_select! { mod uefi; pub use uefi::*; } + target_os = "xous" => { + mod xous; + pub use xous::*; + } target_os = "cygwin" => { mod cygwin; mod windows_prefix; diff --git a/library/std/src/sys/path/xous.rs b/library/std/src/sys/path/xous.rs new file mode 100644 index 0000000000000..f297576179ce4 --- /dev/null +++ b/library/std/src/sys/path/xous.rs @@ -0,0 +1,248 @@ +use crate::ffi::OsStr; +use crate::io; +use crate::path::{Path, PathBuf, Prefix}; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' || b == b':' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn parse_prefix(_: &OsStr) -> Option> { + None +} + +pub const HAS_PREFIXES: bool = false; +pub const MAIN_SEP_STR: &str = "/"; +pub const MAIN_SEP: char = '/'; +pub const ALLOWED_SEP: &[char] = &['/', ':']; + +/// Make a POSIX path absolute without changing its semantics. +pub(crate) fn absolute(path: &Path) -> io::Result { + Ok(path.to_owned()) +} + +pub(crate) fn is_absolute(path: &Path) -> bool { + path.has_root() && path.prefix().is_some() +} + +#[derive(Debug, PartialEq)] +pub enum BasisSplitError { + /// This basis couldn't be split because it's the root element + Root, + /// This basis couldn't be split because it's listing all bases + All, + /// A path component was empty + EmptyComponent, +} + +#[derive(Default, Debug, PartialEq)] +pub struct SplitPath<'a> { + pub basis: Option<&'a str>, + pub dict: Option<&'a str>, + pub key: Option<&'a str>, +} + +/// Split a path into its constituent Basis, Dict, and Key, if the path is legal. +/// A Basis of `None` indicates the default Basis. +pub fn split_basis_dict_key<'a>(src: &'a str) -> Result, BasisSplitError> { + let mut current_string = src; + let mut path = SplitPath::default(); + + // Special case for root + if src == ":" || src == "/" { + return Err(BasisSplitError::Root); + } + // Special case for listing all bases + if src == "::" || src == "//" || src == "/:" || src == ":/" { + return Err(BasisSplitError::All); + } + + // If the string is an absolute path, then the first component is a basis + if let Some(src) = src.strip_prefix(ALLOWED_SEP) { + // See if we can split off a second separator which will get us the end + // of the Basis name + if let Some((maybe_basis, remainder)) = src.split_once(ALLOWED_SEP) { + // If the basis is empty, pull it from the default Basis function. + // Otherwise, use the specified basis. + if !maybe_basis.is_empty() { + path.basis = Some(maybe_basis); + } + current_string = remainder; + } + } + + // Now that we have a fully-specified Basis, pull out the rest of the string. + if let Some(components) = current_string.rsplit_once(ALLOWED_SEP) { + let (mut dict, key) = components; + if let Some(stripped_dict) = dict.strip_prefix(ALLOWED_SEP) { + dict = stripped_dict; + } + // Don't allow empty dicts + if components.0.is_empty() { + return Err(BasisSplitError::EmptyComponent); + } + for path_component in dict.split(ALLOWED_SEP) { + if path_component.is_empty() { + return Err(BasisSplitError::EmptyComponent); + } + } + + path.dict = Some(dict); + // Don't allow empty keys + if !key.is_empty() { + path.key = Some(key); + } + } else if !current_string.is_empty() { + path.dict = Some(current_string) + } + + Ok(path) +} + +#[cfg(test)] +fn assert_basis_dict_key(test: &str, basis: Option<&str>, dict: Option<&str>, key: Option<&str>) { + let path = split_basis_dict_key(test).unwrap(); + assert_eq!(path.basis, basis); + assert_eq!(path.dict, dict); + assert_eq!(path.key, key); +} + +#[test] +fn basis() { + // A basis named "Home Wifi" + assert_basis_dict_key(":Home Wifi:", Some("Home Wifi"), None, None); + + // :.System: -- A basis named ".System" + assert_basis_dict_key(":.System:", Some(".System"), None, None); +} + +#[test] +fn dict() { + // wlan.networks -- A dict named "wlan.networks" in the default basis + assert_basis_dict_key("wlan.networks", None, Some("wlan.networks"), None); +} + +#[test] +fn key() { + // wlan.networks/recent -- A dict named "wlan.networks/recent", which may be considered a path, in the default basis. This also describes a key called "recent" in the dict "wlan.networks", depending on whether + assert_basis_dict_key("wlan.networks/recent", None, Some("wlan.networks"), Some("recent")); +} + +#[test] +fn key_legacy() { + // wlan.networks/recent -- A dict named "wlan.networks:recent", which may be considered a path, in the default basis. This also describes a key called "recent" in the dict "wlan.networks", depending on whether + assert_basis_dict_key("wlan.networks:recent", None, Some("wlan.networks"), Some("recent")); +} + +#[test] +fn dict_in_key() { + // :.System:wlan.networks -- A dict named "wlan.networks" in the basis ".System" + assert_basis_dict_key(":.System:wlan.networks", Some(".System"), Some("wlan.networks"), None); +} + +#[test] +fn basis_dict_key() { + // :.System:wlan.networks/recent -- a fully-qualified path, describing a key "recent" in the dict "wlan.networks" in the basis ".System". + assert_basis_dict_key( + ":.System:wlan.networks/recent", + Some(".System"), + Some("wlan.networks"), + Some("recent"), + ); +} + +#[test] +fn basis_dict_key_legacy() { + // :.System:wlan.networks:recent -- a fully-qualified path, describing a key "recent" in the dict "wlan.networks" in the basis ".System". + assert_basis_dict_key( + ":.System:wlan.networks:recent", + Some(".System"), + Some("wlan.networks"), + Some("recent"), + ); +} + +#[test] +fn root() { + // : -- The root, which lists every basis. Files cannot be created here. "Directories" can be created and destroyed, which corresponds to creating and destroying bases. + assert_eq!(split_basis_dict_key(":"), Err(BasisSplitError::Root)); + assert_eq!(split_basis_dict_key("/"), Err(BasisSplitError::Root)); +} + +#[test] +fn all_bases() { + // :: -- An empty basis is a synonym for all bases, so this corresponds to listing all dicts in the root of the default basis. + assert_eq!(split_basis_dict_key("::"), Err(BasisSplitError::All)); + assert_eq!(split_basis_dict_key("/:"), Err(BasisSplitError::All)); + assert_eq!(split_basis_dict_key(":/"), Err(BasisSplitError::All)); + assert_eq!(split_basis_dict_key("//"), Err(BasisSplitError::All)); +} + +#[test] +fn space_basis() { + // : : -- A basis named " ". Legal, but questionable + assert_basis_dict_key(": :", Some(" "), None, None); +} + +#[test] +fn space_dict() { + // -- A dict named " " in the default basis. Legal, but questionable. + assert_basis_dict_key(" ", None, Some(" "), None); +} + +#[test] +fn legacy_dict_key() { + // : -- A key named " " in a dict called " ". Legal. + assert_basis_dict_key(" : ", None, Some(" "), Some(" ")); +} + +#[test] +fn legacy_trailing_separator() { + // baz: -- A dict named "baz" in the default basis with an extra ":" following. Legal. + assert_basis_dict_key("baz:", None, Some("baz"), None); +} + +#[test] +fn legacy_trailing_separator_two_dicts() { + // baz:foo: -- Currently illegal, but may become equal to baz:foo in the future. + assert_basis_dict_key("baz:foo:", None, Some("baz:foo"), None); +} + +#[test] +fn empty_dict() { + // ::: -- An key named ":" in an empty dict in the default basis. Illegal. + assert_eq!(split_basis_dict_key(":::"), Err(BasisSplitError::EmptyComponent)); +} + +#[test] +fn empty_key_empty_dict() { + // :::: -- An key named "::" in an empty dict in the default basis. Illegal. + assert_eq!(split_basis_dict_key("::::"), Err(BasisSplitError::EmptyComponent)); +} + +#[test] +fn key_in_default_basis() { + // ::foo -- A dict "foo" in the default basis. + assert_basis_dict_key("::foo", None, Some("foo"), None); +} + +#[test] +fn starts_with_slash() { + assert_basis_dict_key("/test/path", Some("test"), Some("path"), None); +} + +#[test] +fn non_legacy_basis() { + assert_basis_dict_key( + "/.System/wlan.networks/recent", + Some(".System"), + Some("wlan.networks"), + Some("recent"), + ); +}