diff --git a/Cargo.lock b/Cargo.lock index 4ed8818cbad97..940608975c5ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1880,9 +1880,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.98" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" dependencies = [ "rustc-std-workspace-core", ] diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 9c957cba6cc09..218fa0344b6a7 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -903,6 +903,7 @@ supported_targets! { ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), + ("riscv32imc-esp-espidf", riscv32imc_esp_espidf), ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu), ("riscv32gc-unknown-linux-musl", riscv32gc_unknown_linux_musl), diff --git a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs new file mode 100644 index 0000000000000..fb084afe960de --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs @@ -0,0 +1,37 @@ +use crate::spec::{LinkerFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), + llvm_target: "riscv32".to_string(), + pointer_width: 32, + arch: "riscv32".to_string(), + + options: TargetOptions { + families: vec!["unix".to_string()], + os: "espidf".to_string(), + env: "newlib".to_string(), + vendor: "espressif".to_string(), + linker_flavor: LinkerFlavor::Gcc, + linker: Some("riscv32-esp-elf-gcc".to_string()), + cpu: "generic-rv32".to_string(), + + // While the RiscV32IMC architecture does not natively support atomics, ESP-IDF does support + // the __atomic* and __sync* GCC builtins, so setting `max_atomic_width` to `Some(32)` + // and `atomic_cas` to `true` will cause the compiler to emit libcalls to these builtins. + // + // Support for atomics is necessary for the Rust STD library, which is supported by the ESP-IDF framework. + max_atomic_width: Some(32), + atomic_cas: true, + + features: "+m,+c".to_string(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 14b454da4f46f..ac7d8c18e3e02 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -45,7 +45,7 @@ cfg_if::cfg_if! { } else if #[cfg(any( all(target_family = "windows", target_env = "gnu"), target_os = "psp", - target_family = "unix", + all(target_family = "unix", not(target_os = "espidf")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { // Rust runtime's startup objects depend on these symbols, so make them public. @@ -58,6 +58,7 @@ cfg_if::cfg_if! { // - arch=wasm32 // - os=none ("bare metal" targets) // - os=uefi + // - os=espidf // - nvptx64-nvidia-cuda // - arch=avr #[path = "dummy.rs"] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 492bffbba74c6..64f413acd9709 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -15,7 +15,7 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.98", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.99", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.44" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/build.rs b/library/std/build.rs index a14ac63c7a8e4..726157c1f1a41 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -26,6 +26,7 @@ fn main() { || target.contains("vxworks") || target.contains("wasm32") || target.contains("asmjs") + || target.contains("espidf") { // These platforms don't have any special requirements. } else { diff --git a/library/std/src/os/espidf/fs.rs b/library/std/src/os/espidf/fs.rs new file mode 100644 index 0000000000000..93dc2c0cab7ee --- /dev/null +++ b/library/std/src/os/espidf/fs.rs @@ -0,0 +1,117 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::espidf::raw; + +/// 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_ext", since = "1.1.0")] + #[rustc_deprecated( + since = "1.8.0", + reason = "deprecated in favor of the accessor \ + methods of this trait" + )] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + #[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_ext2", since = "1.8.0")] + fn st_spare4(&self) -> [u32; 2]; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat as *const raw::stat) } + } + 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_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 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } + fn st_spare4(&self) -> [u32; 2] { + let spare4 = self.as_inner().as_inner().st_spare4; + [spare4[0] as u32, spare4[1] as u32] + } +} diff --git a/library/std/src/os/espidf/mod.rs b/library/std/src/os/espidf/mod.rs new file mode 100644 index 0000000000000..a9cef97093082 --- /dev/null +++ b/library/std/src/os/espidf/mod.rs @@ -0,0 +1,6 @@ +//! Definitions for the ESP-IDF framework. + +#![stable(feature = "raw_ext", since = "1.1.0")] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/espidf/raw.rs b/library/std/src/os/espidf/raw.rs new file mode 100644 index 0000000000000..fb18ec6f6f82a --- /dev/null +++ b/library/std/src/os/espidf/raw.rs @@ -0,0 +1,69 @@ +//! Raw type definitions for the ESP-IDF framework. + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.8.0", + reason = "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" +)] + +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], +} diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 07e29ebf3681c..4c9814919cdfa 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -80,6 +80,8 @@ mod imp { pub mod dragonfly; #[cfg(target_os = "emscripten")] pub mod emscripten; + #[cfg(target_os = "espidf")] + pub mod espidf; #[cfg(target_os = "freebsd")] pub mod freebsd; #[cfg(target_os = "fuchsia")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 73489c0f1620a..6c73d4b21dd3d 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -40,6 +40,8 @@ mod platform { pub use crate::os::dragonfly::*; #[cfg(target_os = "emscripten")] pub use crate::os::emscripten::*; + #[cfg(target_os = "espidf")] + pub use crate::os::espidf::*; #[cfg(target_os = "freebsd")] pub use crate::os::freebsd::*; #[cfg(target_os = "fuchsia")] diff --git a/library/std/src/sys/common/alloc.rs b/library/std/src/sys/common/alloc.rs index 2a54e99020e38..576667c017392 100644 --- a/library/std/src/sys/common/alloc.rs +++ b/library/std/src/sys/common/alloc.rs @@ -14,7 +14,8 @@ use crate::ptr; target_arch = "asmjs", target_arch = "wasm32", target_arch = "hexagon", - target_arch = "riscv32" + target_arch = "riscv32", + target_arch = "xtensa" )))] pub const MIN_ALIGN: usize = 8; #[cfg(all(any( diff --git a/library/std/src/sys/unix/alloc.rs b/library/std/src/sys/unix/alloc.rs index 1b71905aa09b7..7c3d9573940ae 100644 --- a/library/std/src/sys/unix/alloc.rs +++ b/library/std/src/sys/unix/alloc.rs @@ -57,7 +57,8 @@ cfg_if::cfg_if! { target_os = "android", target_os = "illumos", target_os = "redox", - target_os = "solaris" + target_os = "solaris", + target_os = "espidf" ))] { #[inline] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 0bd1ea645779f..ee5e3983ac26a 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -246,3 +246,15 @@ mod imp { Args { iter: res.into_iter() } } } + +#[cfg(target_os = "espidf")] +mod imp { + use super::Args; + + #[inline(always)] + pub unsafe fn init(_argc: isize, _argv: *const *const u8) {} + + pub fn args() -> Args { + Args { iter: Vec::new().into_iter() } + } +} diff --git a/library/std/src/sys/unix/condvar.rs b/library/std/src/sys/unix/condvar.rs index e38f91af9f0b9..61261c0aa84e3 100644 --- a/library/std/src/sys/unix/condvar.rs +++ b/library/std/src/sys/unix/condvar.rs @@ -34,12 +34,23 @@ impl Condvar { ))] pub unsafe fn init(&mut self) {} + // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet + // 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")] + pub unsafe fn init(&mut self) { + let r = libc::pthread_cond_init(self.inner.get(), crate::ptr::null()); + assert_eq!(r, 0); + } + #[cfg(not(any( target_os = "macos", target_os = "ios", target_os = "l4re", target_os = "android", - target_os = "redox" + target_os = "redox", + target_os = "espidf" )))] pub unsafe fn init(&mut self) { use crate::mem::MaybeUninit; @@ -76,7 +87,12 @@ impl Condvar { // where we configure condition variable to use monotonic clock (instead of // default system clock). This approach avoids all problems that result // from changes made to the system time. - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))] + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "espidf" + )))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::mem; @@ -103,7 +119,12 @@ impl Condvar { // This implementation is modeled after libcxx's condition_variable // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "android", + target_os = "espidf" + ))] pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool { use crate::ptr; use crate::time::Instant; diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 3a88dc083b06c..60551aeb3e73e 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -184,3 +184,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "espidf")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "espidf"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 821851a6c65b7..28e32681e15b3 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -91,6 +91,7 @@ impl FileDesc { Ok(ret as usize) } + #[cfg(not(target_os = "espidf"))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let ret = cvt(unsafe { libc::readv( @@ -102,9 +103,14 @@ impl FileDesc { Ok(ret as usize) } + #[cfg(target_os = "espidf")] + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + return crate::io::default_read_vectored(|b| self.read(b), bufs); + } + #[inline] pub fn is_read_vectored(&self) -> bool { - true + cfg!(not(target_os = "espidf")) } pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { @@ -148,6 +154,7 @@ impl FileDesc { Ok(ret as usize) } + #[cfg(not(target_os = "espidf"))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { libc::writev( @@ -159,9 +166,14 @@ impl FileDesc { Ok(ret as usize) } + #[cfg(target_os = "espidf")] + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + return crate::io::default_write_vectored(|b| self.write(b), bufs); + } + #[inline] pub fn is_write_vectored(&self) -> bool { - true + cfg!(not(target_os = "espidf")) } pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { @@ -217,7 +229,7 @@ impl FileDesc { } } #[cfg(any( - target_env = "newlib", + all(target_env = "newlib", not(target_os = "espidf")), target_os = "solaris", target_os = "illumos", target_os = "emscripten", @@ -238,6 +250,12 @@ impl FileDesc { Ok(()) } } + #[cfg(target_os = "espidf")] + 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. + Ok(()) + } #[cfg(target_os = "linux")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { @@ -268,7 +286,17 @@ impl FileDesc { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This // is a POSIX flag that was added to Linux in 2.6.24. - let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?; + #[cfg(not(target_os = "espidf"))] + let cmd = libc::F_DUPFD_CLOEXEC; + + // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics + // will never be supported, as this is a bare metal framework with + // no capabilities for multi-process execution. While F_DUPFD is also + // not supported yet, it might be (currently it returns ENOSYS). + #[cfg(target_os = "espidf")] + let cmd = libc::F_DUPFD; + + let fd = cvt(unsafe { libc::fcntl(self.raw(), cmd, 0) })?; Ok(FileDesc::new(fd)) } } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 7f69ebbeb4de6..fd4defd72eb47 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -312,7 +312,7 @@ impl FileAttr { #[cfg(not(target_os = "netbsd"))] impl FileAttr { - #[cfg(not(target_os = "vxworks"))] + #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] pub fn modified(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_mtime as libc::time_t, @@ -320,7 +320,7 @@ impl FileAttr { })) } - #[cfg(target_os = "vxworks")] + #[cfg(any(target_os = "vxworks", target_os = "espidf"))] pub fn modified(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_mtime as libc::time_t, @@ -328,7 +328,7 @@ impl FileAttr { })) } - #[cfg(not(target_os = "vxworks"))] + #[cfg(all(not(target_os = "vxworks"), not(target_os = "espidf")))] pub fn accessed(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_atime as libc::time_t, @@ -336,7 +336,7 @@ impl FileAttr { })) } - #[cfg(target_os = "vxworks")] + #[cfg(any(target_os = "vxworks", target_os = "espidf"))] pub fn accessed(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_atime as libc::time_t, @@ -609,7 +609,8 @@ impl DirEntry { target_os = "l4re", target_os = "fuchsia", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "espidf" ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 @@ -648,7 +649,8 @@ impl DirEntry { target_os = "emscripten", target_os = "l4re", target_os = "haiku", - target_os = "vxworks" + target_os = "vxworks", + target_os = "espidf" ))] fn name_bytes(&self) -> &[u8] { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } @@ -1106,8 +1108,8 @@ 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"))] { - // VxWorks and Redox lack `linkat`, so use `link` instead. POSIX leaves + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf"))] { + // 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. // Android has `linkat` on newer versions, but we happen to know `link` @@ -1199,6 +1201,18 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> Ok((reader, metadata)) } +#[cfg(target_os = "espidf")] +fn open_to_and_set_permissions( + to: &Path, + reader_metadata: crate::fs::Metadata, +) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { + use crate::fs::OpenOptions; + let writer = OpenOptions::new().open(to)?; + let writer_metadata = writer.metadata()?; + Ok((writer, writer_metadata)) +} + +#[cfg(not(target_os = "espidf"))] fn open_to_and_set_permissions( to: &Path, reader_metadata: crate::fs::Metadata, diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index f827a4bca53f0..f5424e3d28214 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -5,6 +5,7 @@ use crate::io::ErrorKind; pub use self::rand::hashmap_random_keys; pub use libc::strlen; +#[cfg(not(target_os = "espidf"))] #[macro_use] pub mod weak; @@ -43,6 +44,10 @@ pub mod thread_local_dtor; pub mod thread_local_key; pub mod time; +#[cfg(target_os = "espidf")] +pub fn init(argc: isize, argv: *const *const u8) {} + +#[cfg(not(target_os = "espidf"))] // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(argc: isize, argv: *const *const u8) { @@ -304,3 +309,19 @@ cfg_if::cfg_if! { extern "C" {} } } + +#[cfg(target_os = "espidf")] +mod unsupported { + use crate::io; + + pub fn unsupported() -> io::Result { + Err(unsupported_err()) + } + + pub fn unsupported_err() -> io::Error { + io::Error::new_const( + io::ErrorKind::Unsupported, + &"operation not supported on this platform", + ) + } +} diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 753cad55ce745..3f614fde08aca 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -9,7 +9,7 @@ use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; -use libc::{c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}; +use libc::{c_int, c_void, size_t, sockaddr, socklen_t, MSG_PEEK}; pub use crate::sys::{cvt, cvt_r}; @@ -30,13 +30,19 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { // We may need to trigger a glibc workaround. See on_resolver_failure() for details. on_resolver_failure(); - if err == EAI_SYSTEM { + #[cfg(not(target_os = "espidf"))] + if err == libc::EAI_SYSTEM { return Err(io::Error::last_os_error()); } + #[cfg(not(target_os = "espidf"))] let detail = unsafe { str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() }; + + #[cfg(target_os = "espidf")] + let detail = ""; + Err(io::Error::new( io::ErrorKind::Uncategorized, &format!("failed to lookup address information: {}", detail)[..], diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index bbfe846e31556..1d5ffb073211b 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -128,6 +128,12 @@ pub fn error_string(errno: i32) -> String { } } +#[cfg(target_os = "espidf")] +pub fn getcwd() -> io::Result { + Ok(PathBuf::from("/")) +} + +#[cfg(not(target_os = "espidf"))] pub fn getcwd() -> io::Result { let mut buf = Vec::with_capacity(512); loop { @@ -154,6 +160,12 @@ pub fn getcwd() -> io::Result { } } +#[cfg(target_os = "espidf")] +pub fn chdir(p: &path::Path) -> io::Result<()> { + super::unsupported::unsupported() +} + +#[cfg(not(target_os = "espidf"))] pub fn chdir(p: &path::Path) -> io::Result<()> { let p: &OsStr = p.as_ref(); let p = CString::new(p.as_bytes())?; @@ -432,6 +444,11 @@ pub fn current_exe() -> io::Result { path.canonicalize() } +#[cfg(target_os = "espidf")] +pub fn current_exe() -> io::Result { + super::unsupported::unsupported() +} + pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, } @@ -541,6 +558,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> { } } +#[cfg(not(target_os = "espidf"))] pub fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } } @@ -563,7 +581,8 @@ pub fn home_dir() -> Option { target_os = "ios", target_os = "emscripten", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "espidf" ))] unsafe fn fallback() -> Option { None @@ -573,7 +592,8 @@ pub fn home_dir() -> Option { target_os = "ios", target_os = "emscripten", target_os = "redox", - target_os = "vxworks" + target_os = "vxworks", + target_os = "espidf" )))] unsafe fn fallback() -> Option { let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs index b5a19ed54a2f2..0165ece849ee5 100644 --- a/library/std/src/sys/unix/process/mod.rs +++ b/library/std/src/sys/unix/process/mod.rs @@ -13,6 +13,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "vxworks")] { #[path = "process_vxworks.rs"] mod process_inner; + } else if #[cfg(target_os = "espidf")] { + #[path = "process_unsupported.rs"] + mod process_inner; } else { #[path = "process_unix.rs"] mod process_inner; diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs new file mode 100644 index 0000000000000..7d549d060fd88 --- /dev/null +++ b/library/std/src/sys/unix/process/process_unsupported.rs @@ -0,0 +1,122 @@ +use crate::convert::{TryFrom, TryInto}; +use crate::fmt; +use crate::io; +use crate::io::ErrorKind; +use crate::num::NonZeroI32; +use crate::os::raw::NonZero_c_int; +use crate::sys; +use crate::sys::cvt; +use crate::sys::pipe::AnonPipe; +use crate::sys::process::process_common::*; +use crate::sys::unix::unsupported::*; + +use libc::{c_int, pid_t}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn( + &mut self, + default: Stdio, + needs_stdin: bool, + ) -> io::Result<(Process, StdioPipes)> { + unsupported() + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + unsupported_err() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +pub struct Process { + handle: pid_t, +} + +impl Process { + pub fn id(&self) -> u32 { + 0 + } + + pub fn kill(&mut self) -> io::Result<()> { + unsupported() + } + + pub fn wait(&mut self) -> io::Result { + unsupported() + } + + pub fn try_wait(&mut self) -> io::Result> { + unsupported() + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c_int); + +impl ExitStatus { + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn exit_ok(&self) -> Result<(), ExitStatusError> { + Err(ExitStatusError(1.try_into().unwrap())) + } + + pub fn code(&self) -> Option { + None + } + + pub fn signal(&self) -> Option { + None + } + + pub fn core_dumped(&self) -> bool { + false + } + + pub fn stopped_signal(&self) -> Option { + None + } + + pub fn continued(&self) -> bool { + false + } + + pub fn into_raw(&self) -> c_int { + 0 + } +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. +impl From for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a as i32) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "exit code: {}", self.0) + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatusError(NonZero_c_int); + +impl Into for ExitStatusError { + fn into(self) -> ExitStatus { + ExitStatus(self.0.into()) + } +} + +impl ExitStatusError { + pub fn code(self) -> Option { + ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) + } +} diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index 32895001a65ba..7a3f6b0d95a09 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -44,12 +44,17 @@ mod imp { unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) } } - #[cfg(not(any(target_os = "linux", target_os = "android")))] + #[cfg(target_os = "espidf")] + 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")))] fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false } - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "espidf"))] fn getrandom_fill_bytes(v: &mut [u8]) -> bool { use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::os::errno; diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index bc61f472a2b05..133ad3ea420b8 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -9,12 +9,14 @@ use crate::time::Duration; #[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))] use crate::sys::weak::weak; -#[cfg(not(any(target_os = "l4re", target_os = "vxworks")))] +#[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; #[cfg(target_os = "l4re")] pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; #[cfg(target_os = "vxworks")] pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; +#[cfg(target_os = "espidf")] +pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF menuconfig system should be used #[cfg(target_os = "fuchsia")] mod zircon { @@ -50,22 +52,35 @@ impl Thread { let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(libc::pthread_attr_init(&mut attr), 0); - let stack_size = cmp::max(stack, min_stack_size(&attr)); - - match libc::pthread_attr_setstacksize(&mut attr, stack_size) { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = os::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); - } - }; + #[cfg(target_os = "espidf")] + if stack > 0 { + // Only set the stack if a non-zero value is passed + // 0 is used as an indication that the default stack size configured in the ESP-IDF menuconfig system should be used + assert_eq!( + libc::pthread_attr_setstacksize(&mut attr, cmp::max(stack, min_stack_size(&attr))), + 0 + ); + } + + #[cfg(not(target_os = "espidf"))] + { + let stack_size = cmp::max(stack, min_stack_size(&attr)); + + match libc::pthread_attr_setstacksize(&mut attr, stack_size) { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); + } + }; + } let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); // Note: if the thread creation fails and this assert fails, then p will @@ -183,6 +198,7 @@ impl Thread { // Newlib, Emscripten, and VxWorks have no way to set a thread name. } + #[cfg(not(target_os = "espidf"))] pub fn sleep(dur: Duration) { let mut secs = dur.as_secs(); let mut nsecs = dur.subsec_nanos() as _; @@ -208,6 +224,19 @@ impl Thread { } } + #[cfg(target_os = "espidf")] + pub fn sleep(dur: Duration) { + let mut micros = dur.as_micros(); + unsafe { + while micros > 0 { + let st = if micros > u32::MAX as u128 { u32::MAX } else { micros as u32 }; + libc::usleep(st); + + micros -= st as u128; + } + } + } + pub fn join(self) { unsafe { let ret = libc::pthread_join(self.id, ptr::null_mut()); diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index 23a5c81c0053b..7dc09add27fd7 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -361,9 +361,9 @@ mod inner { } } - #[cfg(not(target_os = "dragonfly"))] + #[cfg(not(any(target_os = "dragonfly", target_os = "espidf")))] pub type clock_t = libc::c_int; - #[cfg(target_os = "dragonfly")] + #[cfg(any(target_os = "dragonfly", target_os = "espidf"))] pub type clock_t = libc::c_ulong; fn now(clock: clock_t) -> Timespec { diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs index 7c1d98a5abd59..ea9108f17133a 100644 --- a/library/std/src/sys_common/io.rs +++ b/library/std/src/sys_common/io.rs @@ -1,4 +1,6 @@ -pub const DEFAULT_BUF_SIZE: usize = 8 * 1024; +// Bare metal platforms usually have very small amounts of RAM +// (in the order of hundreds of KB) +pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; #[cfg(test)] #[allow(dead_code)] // not used on emscripten diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 9e52802450682..53b13b9043b3a 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -13,6 +13,7 @@ cfg_if::cfg_if! { } else if #[cfg(any( target_os = "l4re", target_os = "none", + target_os = "espidf", ))] { // These "unix" family members do not have unwinder. // Note this also matches x86_64-unknown-none-linuxkernel. diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 5718a18393f9c..0bfe35ea86189 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -256,6 +256,7 @@ target | std | host | notes `powerpc64le-unknown-linux-musl` | ? | | `riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33) `riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches) +`riscv32imc-esp-espidf` | ✓ | | RISC-V ESP-IDF `riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0) `s390x-unknown-linux-musl` | | | S390x Linux (kernel 2.6.32, MUSL) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux