提交 c3605f8c 编写于 作者: B bors

Auto merge of #95897 - AzureMarker:feature/horizon-std, r=nagisa

STD support for the Nintendo 3DS

Rustc already supports compiling for the Nintendo 3DS using the `armv6k-nintendo-3ds` target (Tier 3). Until now though, only `core` and `alloc` were supported. This PR adds standard library support for the Nintendo 3DS. A notable exclusion is `std::thread` support, which will come in a follow-up PR as it requires more complicated changes.

This has been a joint effort by `@Meziu,` `@ian-h-chamberlain,` myself, and prior work by `@rust3ds` members.

### Background

The Nintendo 3DS (Horizon OS) is a mostly-UNIX looking system, with the caveat that it does not come with a full libc implementation out of the box. On the homebrew side (I'm not under NDA), the libc interface is partially implemented by the [devkitPro](https://devkitpro.org/wiki/devkitPro_pacman) toolchain and a user library like [`libctru`](https://github.com/devkitPro/libctru). This is important because there are [some possible legal barriers](https://github.com/rust-lang/rust/pull/88529#issuecomment-919938396) to linking directly to a library that uses the underlying platform APIs, since they might be considered a trade secret or under NDA.

To get around this, the standard library impl for the 3DS does not directly depend on any platform-level APIs. Instead, it expects standard libc functions to be linked in. The implementation of these libc functions is left to the user. Some functions are provided by the devkitPro toolchain, but in our testing, we used the following to fill in the other functions:
- [`libctru`] - provides more basic APIs, such as `nanosleep`. Linked in by way of [`ctru-sys`](https://github.com/Meziu/ctru-rs/tree/master/ctru-sys).
- [`pthread-3ds`](https://github.com/Meziu/pthread-3ds) - provides pthread APIs for `std::thread`. Implemented using [`libctru`].
- [`linker-fix-3ds`](https://github.com/Meziu/rust-linker-fix-3ds) - fulfills some other missing libc APIs. Implemented using [`libctru`].

For more details, see the `src/doc/rustc/src/platform-support/armv6k-nintendo-3ds.md` file added in this PR.

### Notes
We've already upstreamed changes to the [`libc`] crate to support this PR, as well as the upcoming threading PR. These changes have all been released as of 0.2.121, so we bump the crate version in this PR.
Edit: After some rebases, the version bump has already been merged so it doesn't appear in this PR.

A lot of the changes in this PR are straightforward, and follow in the footsteps of the ESP-IDF target: https://github.com/rust-lang/rust/pull/87666.

The 3DS does not support user space process spawning, so these APIs are unimplemented (similar to ESP-IDF).

[`libctru`]: https://github.com/devkitPro/libctru
[`libc`]: https://github.com/rust-lang/libc
......@@ -37,7 +37,8 @@ pub fn target() -> Target {
pre_link_args,
exe_suffix: ".elf".into(),
no_default_libraries: false,
has_thread_local: true,
// There are some issues in debug builds with this enabled in certain programs.
has_thread_local: false,
..Default::default()
},
}
......
......@@ -143,7 +143,8 @@ mod c_char_definition {
target_arch = "powerpc"
)
),
all(target_os = "fuchsia", target_arch = "aarch64")
all(target_os = "fuchsia", target_arch = "aarch64"),
target_os = "horizon"
))] {
pub type c_char = u8;
pub type NonZero_c_char = crate::num::NonZeroU8;
......
......@@ -29,6 +29,7 @@ fn main() {
|| target.contains("asmjs")
|| target.contains("espidf")
|| target.contains("solid")
|| target.contains("nintendo-3ds")
{
// These platforms don't have any special requirements.
} else {
......
#![stable(feature = "metadata_ext", since = "1.1.0")]
use crate::fs::Metadata;
use crate::sys_common::AsInner;
/// OS-specific extensions to [`fs::Metadata`].
///
/// [`fs::Metadata`]: crate::fs::Metadata
#[stable(feature = "metadata_ext", since = "1.1.0")]
pub trait MetadataExt {
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_dev(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_ino(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_mode(&self) -> u32;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_nlink(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_uid(&self) -> u32;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_gid(&self) -> u32;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_rdev(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_size(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_atime(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_atime_nsec(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_mtime(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_mtime_nsec(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_ctime(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_ctime_nsec(&self) -> i64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_blksize(&self) -> u64;
#[stable(feature = "metadata_ext2", since = "1.8.0")]
fn st_blocks(&self) -> u64;
}
#[stable(feature = "metadata_ext", since = "1.1.0")]
impl MetadataExt for Metadata {
fn st_dev(&self) -> u64 {
self.as_inner().as_inner().st_dev as u64
}
fn st_ino(&self) -> u64 {
self.as_inner().as_inner().st_ino as u64
}
fn st_mode(&self) -> u32 {
self.as_inner().as_inner().st_mode as u32
}
fn st_nlink(&self) -> u64 {
self.as_inner().as_inner().st_nlink as u64
}
fn st_uid(&self) -> u32 {
self.as_inner().as_inner().st_uid as u32
}
fn st_gid(&self) -> u32 {
self.as_inner().as_inner().st_gid as u32
}
fn st_rdev(&self) -> u64 {
self.as_inner().as_inner().st_rdev as u64
}
fn st_size(&self) -> u64 {
self.as_inner().as_inner().st_size as u64
}
fn st_atime(&self) -> i64 {
self.as_inner().as_inner().st_atim.tv_sec
}
fn st_atime_nsec(&self) -> i64 {
self.as_inner().as_inner().st_atim.tv_nsec as i64
}
fn st_mtime(&self) -> i64 {
self.as_inner().as_inner().st_mtim.tv_sec
}
fn st_mtime_nsec(&self) -> i64 {
self.as_inner().as_inner().st_mtim.tv_nsec as i64
}
fn st_ctime(&self) -> i64 {
self.as_inner().as_inner().st_ctim.tv_sec
}
fn st_ctime_nsec(&self) -> i64 {
self.as_inner().as_inner().st_ctim.tv_nsec as i64
}
fn st_blksize(&self) -> u64 {
self.as_inner().as_inner().st_blksize as u64
}
fn st_blocks(&self) -> u64 {
self.as_inner().as_inner().st_blocks as u64
}
}
//! Definitions for Horizon OS
#![stable(feature = "raw_ext", since = "1.1.0")]
pub mod fs;
pub(crate) mod raw;
//! Horizon OS raw type definitions
#![stable(feature = "raw_ext", since = "1.1.0")]
#![deprecated(
since = "1.8.0",
note = "these type aliases are no longer supported by \
the standard library, the `libc` crate on \
crates.io should be used instead for the correct \
definitions"
)]
#![allow(deprecated)]
use crate::os::raw::c_long;
use crate::os::unix::raw::{gid_t, uid_t};
#[stable(feature = "pthread_t", since = "1.8.0")]
pub type pthread_t = libc::pthread_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type blkcnt_t = libc::blkcnt_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type blksize_t = libc::blksize_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type dev_t = libc::dev_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type ino_t = libc::ino_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type mode_t = libc::mode_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type nlink_t = libc::nlink_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type off_t = libc::off_t;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type time_t = libc::time_t;
#[repr(C)]
#[derive(Clone)]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub struct stat {
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_dev: dev_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ino: ino_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mode: mode_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_nlink: nlink_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_uid: uid_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_gid: gid_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_rdev: dev_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_size: off_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_atime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_mtime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_ctime: time_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_blksize: blksize_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_blocks: blkcnt_t,
#[stable(feature = "raw_ext", since = "1.1.0")]
pub st_spare4: [c_long; 2usize],
}
......@@ -123,6 +123,8 @@ pub mod windows {}
pub mod fuchsia;
#[cfg(target_os = "haiku")]
pub mod haiku;
#[cfg(target_os = "horizon")]
pub mod horizon;
#[cfg(target_os = "illumos")]
pub mod illumos;
#[cfg(target_os = "ios")]
......
......@@ -51,6 +51,8 @@ mod platform {
pub use crate::os::fuchsia::*;
#[cfg(target_os = "haiku")]
pub use crate::os::haiku::*;
#[cfg(target_os = "horizon")]
pub use crate::os::horizon::*;
#[cfg(target_os = "illumos")]
pub use crate::os::illumos::*;
#[cfg(target_os = "ios")]
......
......@@ -12,6 +12,16 @@
use crate::sys;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
type UserId = u32;
#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
type GroupId = u32;
#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))]
type UserId = u16;
#[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon"))]
type GroupId = u16;
/// Unix-specific extensions to the [`process::Command`] builder.
///
/// This trait is sealed: it cannot be implemented outside the standard library.
......@@ -22,29 +32,17 @@ pub trait CommandExt: Sealed {
/// `setuid` call in the child process. Failure in the `setuid`
/// call will cause the spawn to fail.
#[stable(feature = "rust1", since = "1.0.0")]
fn uid(
&mut self,
#[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
#[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command;
fn uid(&mut self, id: UserId) -> &mut process::Command;
/// Similar to `uid`, but sets the group ID of the child process. This has
/// the same semantics as the `uid` field.
#[stable(feature = "rust1", since = "1.0.0")]
fn gid(
&mut self,
#[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
#[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command;
fn gid(&mut self, id: GroupId) -> &mut process::Command;
/// Sets the supplementary group IDs for the calling process. Translates to
/// a `setgroups` call in the child process.
#[unstable(feature = "setgroups", issue = "90747")]
fn groups(
&mut self,
#[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] groups: &[u32],
#[cfg(any(target_os = "vxworks", target_os = "espidf"))] groups: &[u16],
) -> &mut process::Command;
fn groups(&mut self, groups: &[GroupId]) -> &mut process::Command;
/// Schedules a closure to be run just before the `exec` function is
/// invoked.
......@@ -158,29 +156,17 @@ fn arg0<S>(&mut self, arg: S) -> &mut process::Command
#[stable(feature = "rust1", since = "1.0.0")]
impl CommandExt for process::Command {
fn uid(
&mut self,
#[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
#[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command {
fn uid(&mut self, id: UserId) -> &mut process::Command {
self.as_inner_mut().uid(id);
self
}
fn gid(
&mut self,
#[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
#[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command {
fn gid(&mut self, id: GroupId) -> &mut process::Command {
self.as_inner_mut().gid(id);
self
}
fn groups(
&mut self,
#[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] groups: &[u32],
#[cfg(any(target_os = "vxworks", target_os = "espidf"))] groups: &[u16],
) -> &mut process::Command {
fn groups(&mut self, groups: &[GroupId]) -> &mut process::Command {
self.as_inner_mut().groups(groups);
self
}
......
......@@ -58,7 +58,8 @@ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut
target_os = "illumos",
target_os = "redox",
target_os = "solaris",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
))] {
#[inline]
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
......
......@@ -68,7 +68,8 @@ fn next_back(&mut self) -> Option<OsString> {
target_os = "l4re",
target_os = "fuchsia",
target_os = "redox",
target_os = "vxworks"
target_os = "vxworks",
target_os = "horizon"
))]
mod imp {
use super::Args;
......
......@@ -119,6 +119,17 @@ pub mod os {
pub const EXE_EXTENSION: &str = "";
}
#[cfg(target_os = "horizon")]
pub mod os {
pub const FAMILY: &str = "unix";
pub const OS: &str = "horizon";
pub const DLL_PREFIX: &str = "lib";
pub const DLL_SUFFIX: &str = ".so";
pub const DLL_EXTENSION: &str = "so";
pub const EXE_SUFFIX: &str = ".elf";
pub const EXE_EXTENSION: &str = "elf";
}
#[cfg(all(target_os = "emscripten", target_arch = "asmjs"))]
pub mod os {
pub const FAMILY: &str = "unix";
......
......@@ -9,8 +9,6 @@
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use libc::{c_int, c_void};
#[cfg(any(
target_os = "android",
target_os = "linux",
......@@ -38,7 +36,7 @@
// larger than or equal to INT_MAX. To handle both of these the read
// size is capped on both platforms.
#[cfg(target_os = "macos")]
const READ_LIMIT: usize = c_int::MAX as usize - 1;
const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
#[cfg(not(target_os = "macos"))]
const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
......@@ -69,6 +67,7 @@ const fn max_iov() -> usize {
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "horizon"
)))]
const fn max_iov() -> usize {
16 // The minimum value required by POSIX.
......@@ -79,33 +78,33 @@ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::read(
self.as_raw_fd(),
buf.as_mut_ptr() as *mut c_void,
buf.as_mut_ptr() as *mut libc::c_void,
cmp::min(buf.len(), READ_LIMIT),
)
})?;
Ok(ret as usize)
}
#[cfg(not(target_os = "espidf"))]
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::readv(
self.as_raw_fd(),
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), max_iov()) as c_int,
cmp::min(bufs.len(), max_iov()) as libc::c_int,
)
})?;
Ok(ret as usize)
}
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
return crate::io::default_read_vectored(|b| self.read(b), bufs);
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
cfg!(not(target_os = "espidf"))
cfg!(not(any(target_os = "espidf", target_os = "horizon")))
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
......@@ -122,7 +121,7 @@ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
unsafe {
cvt(pread64(
self.as_raw_fd(),
buf.as_mut_ptr() as *mut c_void,
buf.as_mut_ptr() as *mut libc::c_void,
cmp::min(buf.len(), READ_LIMIT),
offset as off64_t,
))
......@@ -134,7 +133,7 @@ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
let ret = cvt(unsafe {
libc::read(
self.as_raw_fd(),
buf.unfilled_mut().as_mut_ptr() as *mut c_void,
buf.unfilled_mut().as_mut_ptr() as *mut libc::c_void,
cmp::min(buf.remaining(), READ_LIMIT),
)
})?;
......@@ -151,33 +150,33 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::write(
self.as_raw_fd(),
buf.as_ptr() as *const c_void,
buf.as_ptr() as *const libc::c_void,
cmp::min(buf.len(), READ_LIMIT),
)
})?;
Ok(ret as usize)
}
#[cfg(not(target_os = "espidf"))]
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::writev(
self.as_raw_fd(),
bufs.as_ptr() as *const libc::iovec,
cmp::min(bufs.len(), max_iov()) as c_int,
cmp::min(bufs.len(), max_iov()) as libc::c_int,
)
})?;
Ok(ret as usize)
}
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
return crate::io::default_write_vectored(|b| self.write(b), bufs);
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
cfg!(not(target_os = "espidf"))
cfg!(not(any(target_os = "espidf", target_os = "horizon")))
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
......@@ -189,7 +188,7 @@ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
unsafe {
cvt(pwrite64(
self.as_raw_fd(),
buf.as_ptr() as *const c_void,
buf.as_ptr() as *const libc::c_void,
cmp::min(buf.len(), READ_LIMIT),
offset as off64_t,
))
......@@ -221,7 +220,7 @@ pub fn set_cloexec(&self) -> io::Result<()> {
}
}
#[cfg(any(
all(target_env = "newlib", not(target_os = "espidf")),
all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
target_os = "solaris",
target_os = "illumos",
target_os = "emscripten",
......@@ -242,17 +241,17 @@ pub fn set_cloexec(&self) -> io::Result<()> {
Ok(())
}
}
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
pub fn set_cloexec(&self) -> io::Result<()> {
// FD_CLOEXEC is not supported in ESP-IDF but there's no need to,
// because ESP-IDF does not support spawning processes either.
// FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
// because neither supports spawning processes.
Ok(())
}
#[cfg(target_os = "linux")]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
unsafe {
let v = nonblocking as c_int;
let v = nonblocking as libc::c_int;
cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
Ok(())
}
......
......@@ -395,7 +395,7 @@ pub fn created(&self) -> io::Result<SystemTime> {
#[cfg(not(target_os = "netbsd"))]
impl FileAttr {
#[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
pub fn modified(&self) -> io::Result<SystemTime> {
#[cfg(target_pointer_width = "32")]
cfg_has_statx! {
......@@ -412,7 +412,12 @@ pub fn modified(&self) -> io::Result<SystemTime> {
Ok(SystemTime::new(self.stat.st_mtime as i64, 0))
}
#[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))]
#[cfg(target_os = "horizon")]
pub fn modified(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(self.stat.st_mtim))
}
#[cfg(not(any(target_os = "vxworks", target_os = "espidf", target_os = "horizon")))]
pub fn accessed(&self) -> io::Result<SystemTime> {
#[cfg(target_pointer_width = "32")]
cfg_has_statx! {
......@@ -429,6 +434,11 @@ pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::new(self.stat.st_atime as i64, 0))
}
#[cfg(target_os = "horizon")]
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::from(self.stat.st_atim))
}
#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
......@@ -707,7 +717,8 @@ pub fn file_type(&self) -> io::Result<FileType> {
target_os = "fuchsia",
target_os = "redox",
target_os = "vxworks",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
))]
pub fn ino(&self) -> u64 {
self.entry.d_ino as u64
......@@ -1251,7 +1262,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
let original = cstr(original)?;
let link = cstr(link)?;
cfg_if::cfg_if! {
if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] {
if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon"))] {
// VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
// it implementation-defined whether `link` follows symlinks, so rely on the
// `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
......@@ -1549,14 +1560,14 @@ pub fn chroot(dir: &Path) -> io::Result<()> {
pub use remove_dir_impl::remove_dir_all;
// Fallback for REDOX and ESP-IDF (and Miri)
#[cfg(any(target_os = "redox", target_os = "espidf", miri))]
// Fallback for REDOX, ESP-ID, Horizon, and Miri
#[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri))]
mod remove_dir_impl {
pub use crate::sys_common::fs::remove_dir_all;
}
// Modern implementation using openat(), unlinkat() and fdopendir()
#[cfg(not(any(target_os = "redox", target_os = "espidf", miri)))]
#[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon", miri)))]
mod remove_dir_impl {
use super::{cstr, lstat, Dir, DirEntry, InnerReadDir, ReadDir};
use crate::ffi::CStr;
......
......@@ -47,7 +47,9 @@ unsafe fn init(&mut self) {}
// So on that platform, init() should always be called
// Moreover, that platform does not have pthread_condattr_setclock support,
// hence that initialization should be skipped as well
#[cfg(target_os = "espidf")]
//
// Similar story for the 3DS (horizon).
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
unsafe fn init(&mut self) {
let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null());
assert_eq!(r, 0);
......@@ -59,7 +61,8 @@ unsafe fn init(&mut self) {
target_os = "l4re",
target_os = "android",
target_os = "redox",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
)))]
unsafe fn init(&mut self) {
use crate::mem::MaybeUninit;
......@@ -100,7 +103,8 @@ pub unsafe fn wait(&self, mutex: &Mutex) {
target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
)))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
use crate::mem;
......@@ -132,7 +136,8 @@ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
target_os = "macos",
target_os = "ios",
target_os = "android",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
use crate::ptr;
......
......@@ -88,6 +88,7 @@ unsafe fn sanitize_standard_fds() {
target_os = "ios",
target_os = "redox",
target_os = "l4re",
target_os = "horizon",
)))]
'poll: {
use crate::sys::os::errno;
......@@ -131,6 +132,7 @@ unsafe fn sanitize_standard_fds() {
target_os = "fuchsia",
target_os = "vxworks",
target_os = "l4re",
target_os = "horizon",
)))]
{
use crate::sys::os::errno;
......@@ -149,7 +151,7 @@ unsafe fn sanitize_standard_fds() {
}
unsafe fn reset_sigpipe() {
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
}
}
......@@ -344,7 +346,7 @@ pub fn abort_internal() -> ! {
}
}
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
mod unsupported {
use crate::io;
......
......@@ -446,7 +446,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
path.canonicalize()
}
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
pub fn current_exe() -> io::Result<PathBuf> {
super::unsupported::unsupported()
}
......@@ -601,7 +601,8 @@ pub fn home_dir() -> Option<PathBuf> {
target_os = "emscripten",
target_os = "redox",
target_os = "vxworks",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
))]
unsafe fn fallback() -> Option<OsString> {
None
......@@ -612,7 +613,8 @@ unsafe fn fallback() -> Option<OsString> {
target_os = "emscripten",
target_os = "redox",
target_os = "vxworks",
target_os = "espidf"
target_os = "espidf",
target_os = "horizon"
)))]
unsafe fn fallback() -> Option<OsString> {
let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
......
......@@ -3,6 +3,7 @@
pub use crate::ffi::OsString as EnvKey;
pub use crate::sys_common::process::CommandEnvs;
#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))]
mod process_common;
cfg_if::cfg_if! {
......@@ -13,7 +14,7 @@
} else if #[cfg(target_os = "vxworks")] {
#[path = "process_vxworks.rs"]
mod process_inner;
} else if #[cfg(target_os = "espidf")] {
} else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
#[path = "process_unsupported.rs"]
mod process_inner;
} else {
......
use crate::fmt;
use crate::io;
use crate::io::ErrorKind;
use crate::num::NonZeroI32;
use crate::sys;
use crate::sys::cvt;
use crate::sys::pipe::AnonPipe;
use crate::sys::process::process_common::*;
use crate::sys::unix::unsupported::*;
use core::ffi::NonZero_c_int;
......@@ -18,13 +14,13 @@
impl Command {
pub fn spawn(
&mut self,
default: Stdio,
needs_stdin: bool,
_default: Stdio,
_needs_stdin: bool,
) -> io::Result<(Process, StdioPipes)> {
unsupported()
}
pub fn exec(&mut self, default: Stdio) -> io::Error {
pub fn exec(&mut self, _default: Stdio) -> io::Error {
unsupported_err()
}
}
......@@ -34,7 +30,7 @@ pub fn exec(&mut self, default: Stdio) -> io::Error {
////////////////////////////////////////////////////////////////////////////////
pub struct Process {
handle: pid_t,
_handle: pid_t,
}
impl Process {
......@@ -59,6 +55,7 @@ pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
pub struct ExitStatus(c_int);
impl ExitStatus {
#[cfg_attr(target_os = "horizon", allow(unused))]
pub fn success(&self) -> bool {
self.code() == Some(0)
}
......
......@@ -59,17 +59,27 @@ fn getrandom(
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
}
#[cfg(target_os = "espidf")]
#[cfg(any(target_os = "espidf", target_os = "horizon"))]
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
}
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "espidf")))]
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "espidf",
target_os = "horizon"
)))]
fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool {
false
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "espidf"))]
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "espidf",
target_os = "horizon"
))]
fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sys::os::errno;
......
......@@ -93,7 +93,7 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
}
}
#[cfg(target_os = "vxworks")]
#[cfg(any(target_os = "vxworks", target_os = "horizon"))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
register_dtor_fallback(t, dtor);
......
......@@ -115,7 +115,7 @@ pub unsafe fn new(parker: *mut Parker) {
target_os = "redox"
))] {
addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
} else if #[cfg(target_os = "espidf")] {
} else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] {
let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
assert_eq!(r, 0);
} else {
......
......@@ -18,6 +18,7 @@ pub(in crate::sys::unix) struct Timespec {
}
impl SystemTime {
#[cfg_attr(target_os = "horizon", allow(unused))]
pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime {
SystemTime { t: Timespec::new(tv_sec, tv_nsec) }
}
......@@ -303,9 +304,9 @@ pub fn now() -> SystemTime {
}
}
#[cfg(not(any(target_os = "dragonfly", target_os = "espidf")))]
#[cfg(not(any(target_os = "dragonfly", target_os = "espidf", target_os = "horizon")))]
pub type clock_t = libc::c_int;
#[cfg(any(target_os = "dragonfly", target_os = "espidf"))]
#[cfg(any(target_os = "dragonfly", target_os = "espidf", target_os = "horizon"))]
pub type clock_t = libc::c_ulong;
impl Timespec {
......
......@@ -398,8 +398,20 @@ pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
let (addrp, len) = addr.into_inner();
cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?;
cfg_if::cfg_if! {
if #[cfg(target_os = "horizon")] {
// The 3DS doesn't support a big connection backlog. Sometimes
// it allows up to about 37, but other times it doesn't even
// accept 32. There may be a global limitation causing this.
let backlog = 20;
} else {
// The default for all other platforms
let backlog = 128;
}
}
// Start listening
cvt(unsafe { c::listen(sock.as_raw(), 128) })?;
cvt(unsafe { c::listen(sock.as_raw(), backlog) })?;
Ok(TcpListener { inner: sock })
}
......
......@@ -17,6 +17,7 @@
- [Template for Target-specific Documentation](platform-support/TEMPLATE.md)
- [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)
- [\*-apple-watchos\*](platform-support/apple-watchos.md)
- [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)
- [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
- [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
......
......@@ -226,7 +226,7 @@ target | std | host | notes
`armv5te-unknown-linux-uclibceabi` | ? | | ARMv5TE Linux with uClibc
`armv6-unknown-freebsd` | ✓ | ✓ | ARMv6 FreeBSD
`armv6-unknown-netbsd-eabihf` | ? | |
`armv6k-nintendo-3ds` | * | | ARMv6K Nintendo 3DS, Horizon (Requires devkitARM toolchain)
[`armv6k-nintendo-3ds`](platform-support/armv6k-nintendo-3ds.md) | ? | | ARMv6K Nintendo 3DS, Horizon (Requires devkitARM toolchain)
`armv7-apple-ios` | ✓ | | ARMv7 iOS, Cortex-a8
[`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | ARMv7 Linux with uClibc, softfloat
[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | ARMv7 Linux with uClibc, hardfloat
......
# `armv6k-nintendo-3ds`
**Tier: 3**
The Nintendo 3DS platform, which has an ARMv6K processor, and its associated
operating system (`horizon`).
Rust support for this target is not affiliated with Nintendo, and is not derived
from nor used with any official Nintendo SDK.
## Target maintainers
- [@Meziu](https://github.com/Meziu)
- [@AzureMarker](https://github.com/AzureMarker)
- [@ian-h-chamberlain](https://github.com/ian-h-chamberlain)
## Requirements
This target is cross-compiled. Dynamic linking is not supported.
`#![no_std]` crates can be built using `build-std` to build `core` and optionally
`alloc`, and either `panic_abort` or `panic_unwind`.
`std` is partially supported, but mostly works. Some APIs are unimplemented
and will simply return an error, such as `std::process`. An allocator is provided
by default.
In order to support some APIs, binaries must be linked against `libc` written
for the target, using a linker for the target. These are provided by the
devkitARM toolchain. See
[Cross-compilation toolchains and C code](#cross-compilation-toolchains-and-c-code)
for more details.
Additionally, some helper crates provide implementations of some `libc` functions
use by `std` that may otherwise be missing. These, or an alternate implementation
of the relevant functions, are required to use `std`:
- [`pthread-3ds`](https://github.com/Meziu/pthread-3ds) provides pthread APIs for `std::thread`.
- [`linker-fix-3ds`](https://github.com/Meziu/rust-linker-fix-3ds) fulfills some other missing libc APIs.
Binaries built for this target should be compatible with all variants of the
3DS (and 2DS) hardware and firmware, but testing is limited and some versions may
not work correctly.
This target generates binaries in the ELF format.
## Building the target
You can build Rust with support for the target by adding it to the `target`
list in `config.toml` and providing paths to the devkitARM toolchain.
```toml
[build]
build-stage = 1
target = ["armv6k-nintendo-3ds"]
[target.armv6k-nintendo-3ds]
cc = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-gcc"
cxx = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-g++"
ar = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-ar"
ranlib = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-ranlib"
linker = "/opt/devkitpro/devkitARM/bin/arm-none-eabi-gcc"
```
Also, to build `compiler_builtins` for the target, export these flags before
building the Rust toolchain:
```sh
export CFLAGS_armv6k_nintendo_3ds="-mfloat-abi=hard -mtune=mpcore -mtp=soft -march=armv6k"
```
## Building Rust programs
Rust does not yet ship pre-compiled artifacts for this target.
The recommended way to build binaries is by using the
[cargo-3ds](https://github.com/Meziu/cargo-3ds) tool, which uses `build-std`
and provides commands that work like the usual `cargo run`, `cargo build`, etc.
You can also build Rust with the target enabled (see
[Building the target](#building-the-target) above).
As mentioned in [Requirements](#requirements), programs that use `std` must link
against both the devkitARM toolchain and libraries providing the `libc` APIs used
in `std`. There is a general-purpose utility crate for working with nonstandard
APIs provided by the OS: [`ctru-rs`](https://github.com/Meziu/ctru-rs).
Add it to Cargo.toml to use it in your program:
```toml
[dependencies]
ctru-rs = { git = "https://github.com/Meziu/ctru-rs.git" }
```
Using this library's `init()` function ensures the symbols needed to link
against `std` are present (as mentioned in [Requirements](#requirements)
above), as well as providing a runtime suitable for `std`:
```rust,ignore (requires-3rd-party-library)
fn main() {
ctru::init();
}
```
## Testing
Binaries built for this target can be run in an emulator (most commonly
[Citra](https://citra-emu.org/)), or sent to a device through
the use of a tool like devkitARM's `3dslink`. They may also simply be copied
to an SD card to be inserted in the device.
The `cargo-3ds` tool mentioned in [Building Rust programs](#building-rust-programs)
supports the use of `3dslink` with `cargo 3ds run`. The default Rust test runner
is not supported, but
[custom test frameworks](https://doc.rust-lang.org/beta/unstable-book/language-features/custom-test-frameworks.html)
can be used with `cargo 3ds test` to run unit tests on a device.
The Rust test suite for `library/std` is not yet supported.
## Cross-compilation toolchains and C code
C code can be built for this target using the
[devkitARM toolchain](https://devkitpro.org/wiki/Getting_Started).
This toolchain provides `arm-none-eabi-gcc` as the linker used to link Rust
programs as well.
The toolchain also provides a `libc` implementation, which is required by `std`
for many of its APIs, and a helper library `libctru` which is used by several
of the helper crates listed in [Requirements](#requirements).
This toolchain does not, however, include all of the APIs expected by `std`,
and the remaining APIs are implemented by `pthread-3ds` and `linker-fix-3ds`.
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册