about summary refs log tree commit diff
diff options
context:
space:
mode:
author袁浩 <yuanhao34@huawei.com>2023-10-09 11:27:11 +0800
committerSword-Destiny <yuanhonglong@outlook.com>2023-12-07 10:33:03 +0800
commite353eb91fb793c5ec7cb12f7c99a13d55e05fba1 (patch)
tree6a61dd5284f4cb8c425316e9e510190ef7744c1e
parent28968414c57f12b827f01a5e6b99017b7ffa7370 (diff)
downloadrust-e353eb91fb793c5ec7cb12f7c99a13d55e05fba1.tar.gz
rust-e353eb91fb793c5ec7cb12f7c99a13d55e05fba1.zip
add teeos std impl
Signed-off-by: 袁浩 <yuanhao34@huawei.com>
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs1
-rw-r--r--library/panic_abort/src/lib.rs10
-rw-r--r--library/std/build.rs1
-rw-r--r--library/std/src/sys/mod.rs3
-rw-r--r--library/std/src/sys/teeos/alloc.rs57
-rw-r--r--library/std/src/sys/teeos/locks/condvar.rs100
-rw-r--r--library/std/src/sys/teeos/locks/mod.rs8
-rw-r--r--library/std/src/sys/teeos/locks/rwlock.rs44
-rw-r--r--library/std/src/sys/teeos/mod.rs167
-rw-r--r--library/std/src/sys/teeos/net.rs372
-rw-r--r--library/std/src/sys/teeos/os.rs134
-rw-r--r--library/std/src/sys/teeos/rand.rs21
-rw-r--r--library/std/src/sys/teeos/stdio.rs88
-rw-r--r--library/std/src/sys/teeos/thread.rs164
-rw-r--r--library/std/src/sys/teeos/thread_local_dtor.rs4
-rw-r--r--library/std/src/sys/unix/time.rs12
-rw-r--r--library/std/src/thread/mod.rs1
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md13
18 files changed, 1190 insertions, 10 deletions
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs
index eec2668d487..b490e80258c 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_teeos.rs
@@ -4,7 +4,6 @@ pub fn target() -> Target {
     let mut base = base::teeos::opts();
     base.features = "+strict-align,+neon,+fp-armv8".into();
     base.max_atomic_width = Some(128);
-    base.linker = Some("aarch64-linux-gnu-ld".into());
 
     Target {
         llvm_target: "aarch64-unknown-none".into(),
diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs
index 6e097e2caf2..8fd64279ac5 100644
--- a/library/panic_abort/src/lib.rs
+++ b/library/panic_abort/src/lib.rs
@@ -81,6 +81,16 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
                 }
                 core::intrinsics::unreachable();
             }
+        } else if #[cfg(target_os = "teeos")] {
+            mod teeos {
+                extern "C" {
+                    pub fn TEE_Panic(code: u32) -> !;
+                }
+            }
+
+            unsafe fn abort() -> ! {
+                teeos::TEE_Panic(1);
+            }
         } else {
             unsafe fn abort() -> ! {
                 core::intrinsics::abort();
diff --git a/library/std/build.rs b/library/std/build.rs
index 11ba29766c1..0f5068b59b7 100644
--- a/library/std/build.rs
+++ b/library/std/build.rs
@@ -34,6 +34,7 @@ fn main() {
         || target.contains("xous")
         || target.contains("hurd")
         || target.contains("uefi")
+        || target.contains("teeos")
         // See src/bootstrap/synthetic_targets.rs
         || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()
     {
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index 159ffe7ac96..88420bd3612 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -53,6 +53,9 @@ cfg_if::cfg_if! {
     } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
         mod sgx;
         pub use self::sgx::*;
+    } else if #[cfg(target_os = "teeos")] {
+        mod teeos;
+        pub use self::teeos::*;
     } else {
         mod unsupported;
         pub use self::unsupported::*;
diff --git a/library/std/src/sys/teeos/alloc.rs b/library/std/src/sys/teeos/alloc.rs
new file mode 100644
index 00000000000..e236819aa23
--- /dev/null
+++ b/library/std/src/sys/teeos/alloc.rs
@@ -0,0 +1,57 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr;
+use crate::sys::common::alloc::{realloc_fallback, MIN_ALIGN};
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+    #[inline]
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        // jemalloc provides alignment less than MIN_ALIGN for small allocations.
+        // So only rely on MIN_ALIGN if size >= align.
+        // Also see <https://github.com/rust-lang/rust/issues/45955> and
+        // <https://github.com/rust-lang/rust/issues/62251#issuecomment-507580914>.
+        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+            libc::malloc(layout.size()) as *mut u8
+        } else {
+            aligned_malloc(&layout)
+        }
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+        // See the comment above in `alloc` for why this check looks the way it does.
+        if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+            libc::calloc(layout.size(), 1) as *mut u8
+        } else {
+            let ptr = self.alloc(layout);
+            if !ptr.is_null() {
+                ptr::write_bytes(ptr, 0, layout.size());
+            }
+            ptr
+        }
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
+        libc::free(ptr as *mut libc::c_void)
+    }
+
+    #[inline]
+    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+        if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
+            libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
+        } else {
+            realloc_fallback(self, ptr, layout, new_size)
+        }
+    }
+}
+
+#[inline]
+unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
+    let mut out = ptr::null_mut();
+    // posix_memalign requires that the alignment be a multiple of `sizeof(void*)`.
+    // Since these are all powers of 2, we can just use max.
+    let align = layout.align().max(crate::mem::size_of::<usize>());
+    let ret = libc::posix_memalign(&mut out, align, layout.size());
+    if ret != 0 { ptr::null_mut() } else { out as *mut u8 }
+}
diff --git a/library/std/src/sys/teeos/locks/condvar.rs b/library/std/src/sys/teeos/locks/condvar.rs
new file mode 100644
index 00000000000..c08e8145b8c
--- /dev/null
+++ b/library/std/src/sys/teeos/locks/condvar.rs
@@ -0,0 +1,100 @@
+use crate::cell::UnsafeCell;
+use crate::ptr;
+use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
+use crate::sys::locks::mutex::{self, Mutex};
+use crate::sys::time::TIMESPEC_MAX;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::time::Duration;
+
+extern "C" {
+    pub fn pthread_cond_timedwait(
+        cond: *mut libc::pthread_cond_t,
+        lock: *mut libc::pthread_mutex_t,
+        adstime: *const libc::timespec,
+    ) -> libc::c_int;
+}
+
+struct AllocatedCondvar(UnsafeCell<libc::pthread_cond_t>);
+
+pub struct Condvar {
+    inner: LazyBox<AllocatedCondvar>,
+    mutex: AtomicPtr<libc::pthread_mutex_t>,
+}
+
+#[inline]
+fn raw(c: &Condvar) -> *mut libc::pthread_cond_t {
+    c.inner.0.get()
+}
+
+unsafe impl Send for AllocatedCondvar {}
+unsafe impl Sync for AllocatedCondvar {}
+
+impl LazyInit for AllocatedCondvar {
+    fn init() -> Box<Self> {
+        let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)));
+
+        let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) };
+        assert_eq!(r, 0);
+
+        condvar
+    }
+}
+
+impl Drop for AllocatedCondvar {
+    #[inline]
+    fn drop(&mut self) {
+        let r = unsafe { libc::pthread_cond_destroy(self.0.get()) };
+        debug_assert_eq!(r, 0);
+    }
+}
+
+impl Condvar {
+    pub const fn new() -> Condvar {
+        Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) }
+    }
+
+    #[inline]
+    fn verify(&self, mutex: *mut libc::pthread_mutex_t) {
+        match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) {
+            Ok(_) => {}                // Stored the address
+            Err(n) if n == mutex => {} // Lost a race to store the same address
+            _ => panic!("attempted to use a condition variable with two mutexes"),
+        }
+    }
+
+    #[inline]
+    pub fn notify_one(&self) {
+        let r = unsafe { libc::pthread_cond_signal(raw(self)) };
+        debug_assert_eq!(r, 0);
+    }
+
+    #[inline]
+    pub fn notify_all(&self) {
+        let r = unsafe { libc::pthread_cond_broadcast(raw(self)) };
+        debug_assert_eq!(r, 0);
+    }
+
+    #[inline]
+    pub unsafe fn wait(&self, mutex: &Mutex) {
+        let mutex = mutex::raw(mutex);
+        self.verify(mutex);
+        let r = libc::pthread_cond_wait(raw(self), mutex);
+        debug_assert_eq!(r, 0);
+    }
+
+    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+        use crate::sys::time::Timespec;
+
+        let mutex = mutex::raw(mutex);
+        self.verify(mutex);
+
+        let timeout = Timespec::now(libc::CLOCK_MONOTONIC)
+            .checked_add_duration(&dur)
+            .and_then(|t| t.to_timespec())
+            .unwrap_or(TIMESPEC_MAX);
+
+        let r = pthread_cond_timedwait(raw(self), mutex, &timeout);
+        assert!(r == libc::ETIMEDOUT || r == 0);
+        r == 0
+    }
+}
diff --git a/library/std/src/sys/teeos/locks/mod.rs b/library/std/src/sys/teeos/locks/mod.rs
new file mode 100644
index 00000000000..c58e9c7fd45
--- /dev/null
+++ b/library/std/src/sys/teeos/locks/mod.rs
@@ -0,0 +1,8 @@
+pub mod condvar;
+#[path = "../../unix/locks/pthread_mutex.rs"]
+pub mod mutex;
+pub mod rwlock;
+
+pub(crate) use condvar::Condvar;
+pub(crate) use mutex::Mutex;
+pub(crate) use rwlock::RwLock;
diff --git a/library/std/src/sys/teeos/locks/rwlock.rs b/library/std/src/sys/teeos/locks/rwlock.rs
new file mode 100644
index 00000000000..27cdb88788f
--- /dev/null
+++ b/library/std/src/sys/teeos/locks/rwlock.rs
@@ -0,0 +1,44 @@
+use crate::sys::locks::mutex::Mutex;
+
+/// we do not supported rwlock, so use mutex to simulate rwlock.
+/// it's useful because so many code in std will use rwlock.
+pub struct RwLock {
+    inner: Mutex,
+}
+
+impl RwLock {
+    #[inline]
+    pub const fn new() -> RwLock {
+        RwLock { inner: Mutex::new() }
+    }
+
+    #[inline]
+    pub fn read(&self) {
+        unsafe { self.inner.lock() };
+    }
+
+    #[inline]
+    pub fn try_read(&self) -> bool {
+        unsafe { self.inner.try_lock() }
+    }
+
+    #[inline]
+    pub fn write(&self) {
+        unsafe { self.inner.lock() };
+    }
+
+    #[inline]
+    pub unsafe fn try_write(&self) -> bool {
+        unsafe { self.inner.try_lock() }
+    }
+
+    #[inline]
+    pub unsafe fn read_unlock(&self) {
+        unsafe { self.inner.unlock() };
+    }
+
+    #[inline]
+    pub unsafe fn write_unlock(&self) {
+        unsafe { self.inner.unlock() };
+    }
+}
diff --git a/library/std/src/sys/teeos/mod.rs b/library/std/src/sys/teeos/mod.rs
new file mode 100644
index 00000000000..ed8c54b2c36
--- /dev/null
+++ b/library/std/src/sys/teeos/mod.rs
@@ -0,0 +1,167 @@
+//! System bindings for the Teeos platform
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for Teeos.
+#![allow(unsafe_op_in_unsafe_fn)]
+#![allow(unused_variables)]
+#![allow(dead_code)]
+
+pub use self::rand::hashmap_random_keys;
+
+pub mod alloc;
+#[path = "../unsupported/args.rs"]
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+#[path = "../unsupported/env.rs"]
+pub mod env;
+pub mod locks;
+//pub mod fd;
+#[path = "../unsupported/fs.rs"]
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+#[path = "../unix/memchr.rs"]
+pub mod memchr;
+pub mod net;
+#[path = "../unsupported/once.rs"]
+pub mod once;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+mod rand;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_dtor;
+#[path = "../unix/thread_local_key.rs"]
+pub mod thread_local_key;
+#[path = "../unsupported/thread_parking.rs"]
+pub mod thread_parking;
+#[allow(non_upper_case_globals)]
+#[path = "../unix/time.rs"]
+pub mod time;
+
+use crate::io::ErrorKind;
+
+pub fn abort_internal() -> ! {
+    unsafe { libc::abort() }
+}
+
+// Trusted Applications are loaded as dynamic libraries on Teeos,
+// so this should never be called.
+pub fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {}
+
+// SAFETY: must be called only once during runtime cleanup.
+// this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+    unimplemented!()
+    // We do NOT have stack overflow handler, because TEE OS will kill TA when it happens.
+    // So cleanup is commented
+    // stack_overflow::cleanup();
+}
+
+#[inline]
+pub(crate) fn is_interrupted(errno: i32) -> bool {
+    errno == libc::EINTR
+}
+
+// Note: code below is 1:1 copied from unix/mod.rs
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+    use ErrorKind::*;
+    match errno as libc::c_int {
+        libc::E2BIG => ArgumentListTooLong,
+        libc::EADDRINUSE => AddrInUse,
+        libc::EADDRNOTAVAIL => AddrNotAvailable,
+        libc::EBUSY => ResourceBusy,
+        libc::ECONNABORTED => ConnectionAborted,
+        libc::ECONNREFUSED => ConnectionRefused,
+        libc::ECONNRESET => ConnectionReset,
+        libc::EDEADLK => Deadlock,
+        libc::EDQUOT => FilesystemQuotaExceeded,
+        libc::EEXIST => AlreadyExists,
+        libc::EFBIG => FileTooLarge,
+        libc::EHOSTUNREACH => HostUnreachable,
+        libc::EINTR => Interrupted,
+        libc::EINVAL => InvalidInput,
+        libc::EISDIR => IsADirectory,
+        libc::ELOOP => FilesystemLoop,
+        libc::ENOENT => NotFound,
+        libc::ENOMEM => OutOfMemory,
+        libc::ENOSPC => StorageFull,
+        libc::ENOSYS => Unsupported,
+        libc::EMLINK => TooManyLinks,
+        libc::ENAMETOOLONG => InvalidFilename,
+        libc::ENETDOWN => NetworkDown,
+        libc::ENETUNREACH => NetworkUnreachable,
+        libc::ENOTCONN => NotConnected,
+        libc::ENOTDIR => NotADirectory,
+        libc::ENOTEMPTY => DirectoryNotEmpty,
+        libc::EPIPE => BrokenPipe,
+        libc::EROFS => ReadOnlyFilesystem,
+        libc::ESPIPE => NotSeekable,
+        libc::ESTALE => StaleNetworkFileHandle,
+        libc::ETIMEDOUT => TimedOut,
+        libc::ETXTBSY => ExecutableFileBusy,
+        libc::EXDEV => CrossesDevices,
+
+        libc::EACCES | libc::EPERM => PermissionDenied,
+
+        // These two constants can have the same value on some systems,
+        // but different values on others, so we can't use a match
+        // clause
+        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock,
+
+        _ => Uncategorized,
+    }
+}
+
+#[doc(hidden)]
+pub trait IsMinusOne {
+    fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+    ($($t:ident)*) => ($(impl IsMinusOne for $t {
+        fn is_minus_one(&self) -> bool {
+            *self == -1
+        }
+    })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> {
+    if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) }
+}
+
+pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T>
+where
+    T: IsMinusOne,
+    F: FnMut() -> T,
+{
+    loop {
+        match cvt(f()) {
+            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+            other => return other,
+        }
+    }
+}
+
+pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> {
+    if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) }
+}
+
+use crate::io as std_io;
+pub fn unsupported<T>() -> std_io::Result<T> {
+    Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> std_io::Error {
+    std_io::Error::new(std_io::ErrorKind::Unsupported, "operation not supported on this platform")
+}
diff --git a/library/std/src/sys/teeos/net.rs b/library/std/src/sys/teeos/net.rs
new file mode 100644
index 00000000000..0df681dbfa5
--- /dev/null
+++ b/library/std/src/sys/teeos/net.rs
@@ -0,0 +1,372 @@
+use crate::fmt;
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::sys::unsupported;
+use crate::time::Duration;
+
+pub struct TcpStream(!);
+
+impl TcpStream {
+    pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+        unsupported()
+    }
+
+    pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+        unsupported()
+    }
+
+    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+        self.0
+    }
+
+    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+        self.0
+    }
+
+    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn read(&self, _: &mut [u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn is_read_vectored(&self) -> bool {
+        self.0
+    }
+
+    pub fn write(&self, _: &[u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn is_write_vectored(&self) -> bool {
+        self.0
+    }
+
+    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+        self.0
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        self.0
+    }
+
+    pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn duplicate(&self) -> io::Result<TcpStream> {
+        self.0
+    }
+
+    pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn linger(&self) -> io::Result<Option<Duration>> {
+        self.0
+    }
+
+    pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn nodelay(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        self.0
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.0
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+}
+
+impl fmt::Debug for TcpStream {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+pub struct TcpListener(!);
+
+impl TcpListener {
+    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+        unsupported()
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        self.0
+    }
+
+    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+        self.0
+    }
+
+    pub fn duplicate(&self) -> io::Result<TcpListener> {
+        self.0
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        self.0
+    }
+
+    pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn only_v6(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.0
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+}
+
+impl fmt::Debug for TcpListener {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+pub struct UdpSocket(!);
+
+impl UdpSocket {
+    pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+        unsupported()
+    }
+
+    pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+        self.0
+    }
+
+    pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+        self.0
+    }
+
+    pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.0
+    }
+
+    pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+        self.0
+    }
+
+    pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn duplicate(&self) -> io::Result<UdpSocket> {
+        self.0
+    }
+
+    pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+        self.0
+    }
+
+    pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+        self.0
+    }
+
+    pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn broadcast(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+        self.0
+    }
+
+    pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn ttl(&self) -> io::Result<u32> {
+        self.0
+    }
+
+    pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+        self.0
+    }
+
+    pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+        self.0
+    }
+}
+
+impl fmt::Debug for UdpSocket {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+    pub fn port(&self) -> u16 {
+        self.0
+    }
+}
+
+impl Iterator for LookupHost {
+    type Item = SocketAddr;
+    fn next(&mut self) -> Option<SocketAddr> {
+        self.0
+    }
+}
+
+impl TryFrom<&str> for LookupHost {
+    type Error = io::Error;
+
+    fn try_from(_v: &str) -> io::Result<LookupHost> {
+        unsupported()
+    }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+    type Error = io::Error;
+
+    fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+        unsupported()
+    }
+}
+
+#[allow(nonstandard_style)]
+pub mod netc {
+    pub const AF_INET: u8 = 0;
+    pub const AF_INET6: u8 = 1;
+    pub type sa_family_t = u8;
+
+    #[derive(Copy, Clone)]
+    pub struct in_addr {
+        pub s_addr: u32,
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct sockaddr_in {
+        pub sin_family: sa_family_t,
+        pub sin_port: u16,
+        pub sin_addr: in_addr,
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct in6_addr {
+        pub s6_addr: [u8; 16],
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct sockaddr_in6 {
+        pub sin6_family: sa_family_t,
+        pub sin6_port: u16,
+        pub sin6_addr: in6_addr,
+        pub sin6_flowinfo: u32,
+        pub sin6_scope_id: u32,
+    }
+
+    #[derive(Copy, Clone)]
+    pub struct sockaddr {}
+}
+
+pub type Socket = UdpSocket;
diff --git a/library/std/src/sys/teeos/os.rs b/library/std/src/sys/teeos/os.rs
new file mode 100644
index 00000000000..e54a92f01f8
--- /dev/null
+++ b/library/std/src/sys/teeos/os.rs
@@ -0,0 +1,134 @@
+//! Implementation of `std::os` functionality for teeos
+
+use core::marker::PhantomData;
+
+use crate::error::Error as StdError;
+use crate::ffi::{OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::path;
+use crate::path::PathBuf;
+
+use super::unsupported;
+
+pub fn errno() -> i32 {
+    unsafe { (*libc::__errno_location()) as i32 }
+}
+
+// Hardcoded to return 4096, since `sysconf` is only implemented as a stub.
+pub fn page_size() -> usize {
+    // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
+    4096
+}
+
+// Everything below are stubs and copied from unsupported.rs
+
+pub fn error_string(_errno: i32) -> String {
+    "error string unimplemented".to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+    panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+    type Item = PathBuf;
+    fn next(&mut self) -> Option<PathBuf> {
+        self.0
+    }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+    I: Iterator<Item = T>,
+    T: AsRef<OsStr>,
+{
+    Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        "not supported on this platform yet".fmt(f)
+    }
+}
+
+impl StdError for JoinPathsError {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "not supported on this platform yet"
+    }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub struct Env(!);
+
+impl Env {
+    // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
+    pub fn str_debug(&self) -> impl fmt::Debug + '_ {
+        let Self(inner) = self;
+        match *inner {}
+    }
+}
+
+impl fmt::Debug for Env {
+    fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Self(inner) = self;
+        match *inner {}
+    }
+}
+
+impl Iterator for Env {
+    type Item = (OsString, OsString);
+    fn next(&mut self) -> Option<(OsString, OsString)> {
+        let Self(inner) = self;
+        match *inner {}
+    }
+}
+
+pub fn env() -> Env {
+    panic!("not supported on this platform")
+}
+
+pub fn getenv(_: &OsStr) -> Option<OsString> {
+    None
+}
+
+pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
+    Err(io::Error::new(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
+}
+
+pub fn unsetenv(_: &OsStr) -> io::Result<()> {
+    Err(io::Error::new(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
+}
+
+pub fn temp_dir() -> PathBuf {
+    panic!("no filesystem on this platform")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+    None
+}
+
+pub fn exit(_code: i32) -> ! {
+    panic!("TA should not call `exit`")
+}
+
+pub fn getpid() -> u32 {
+    panic!("no pids on this platform")
+}
diff --git a/library/std/src/sys/teeos/rand.rs b/library/std/src/sys/teeos/rand.rs
new file mode 100644
index 00000000000..b45c3bb40e7
--- /dev/null
+++ b/library/std/src/sys/teeos/rand.rs
@@ -0,0 +1,21 @@
+pub fn hashmap_random_keys() -> (u64, u64) {
+    const KEY_LEN: usize = core::mem::size_of::<u64>();
+
+    let mut v = [0u8; KEY_LEN * 2];
+    imp::fill_bytes(&mut v);
+
+    let key1 = v[0..KEY_LEN].try_into().unwrap();
+    let key2 = v[KEY_LEN..].try_into().unwrap();
+
+    (u64::from_ne_bytes(key1), u64::from_ne_bytes(key2))
+}
+
+mod imp {
+    extern "C" {
+        fn TEE_GenerateRandom(randomBuffer: *mut core::ffi::c_void, randomBufferLen: libc::size_t);
+    }
+
+    pub fn fill_bytes(v: &mut [u8]) {
+        unsafe { TEE_GenerateRandom(v.as_mut_ptr() as _, v.len() * crate::mem::size_of::<u8>()) }
+    }
+}
diff --git a/library/std/src/sys/teeos/stdio.rs b/library/std/src/sys/teeos/stdio.rs
new file mode 100644
index 00000000000..9ca04f29273
--- /dev/null
+++ b/library/std/src/sys/teeos/stdio.rs
@@ -0,0 +1,88 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::io;
+use core::arch::asm;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+const KCALL_DEBUG_CMD_PUT_BYTES: i64 = 2;
+
+unsafe fn debug_call(cap_ref: u64, call_no: i64, arg1: u64, arg2: u64) -> i32 {
+    let ret: u64;
+    unsafe {
+        asm!(
+            "svc #99",
+            inout("x0") cap_ref => ret,
+            in("x1") call_no,
+            in("x2") arg1,
+            in("x3") arg2,
+        );
+    }
+
+    ret as i32
+}
+
+fn print_buf(s: &[u8]) -> io::Result<usize> {
+    // Corresponds to `HM_DEBUG_PUT_BYTES_LIMIT`.
+    const MAX_LEN: usize = 512;
+    let len = if s.len() > MAX_LEN { MAX_LEN } else { s.len() };
+    let result = unsafe { debug_call(0, KCALL_DEBUG_CMD_PUT_BYTES, s.as_ptr() as u64, len as u64) };
+
+    if result == 0 { Ok(len) } else { Err(io::Error::from(io::ErrorKind::InvalidInput)) }
+}
+
+impl Stdin {
+    pub const fn new() -> Stdin {
+        Stdin
+    }
+}
+
+impl io::Read for Stdin {
+    fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
+        Ok(0)
+    }
+}
+
+impl Stdout {
+    pub const fn new() -> Stdout {
+        Stdout
+    }
+}
+
+impl io::Write for Stdout {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        print_buf(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Stderr {
+    pub const fn new() -> Stderr {
+        Stderr
+    }
+}
+
+impl io::Write for Stderr {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        print_buf(buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+    err.raw_os_error() == Some(libc::EBADF as i32)
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    Some(Stderr::new())
+}
diff --git a/library/std/src/sys/teeos/thread.rs b/library/std/src/sys/teeos/thread.rs
new file mode 100644
index 00000000000..155f333f906
--- /dev/null
+++ b/library/std/src/sys/teeos/thread.rs
@@ -0,0 +1,164 @@
+use core::convert::TryInto;
+
+use crate::cmp;
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::ptr;
+use crate::sys::os;
+use crate::time::Duration;
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024;
+
+pub struct Thread {
+    id: libc::pthread_t,
+}
+
+// Some platforms may have pthread_t as a pointer in which case we still want
+// a thread to be Send/Sync
+unsafe impl Send for Thread {}
+unsafe impl Sync for Thread {}
+
+extern "C" {
+    pub fn TEE_Wait(timeout: u32) -> u32;
+}
+
+impl Thread {
+    // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+        let p = Box::into_raw(Box::new(p));
+        let mut native: libc::pthread_t = mem::zeroed();
+        let mut attr: libc::pthread_attr_t = mem::zeroed();
+        assert_eq!(libc::pthread_attr_init(&mut attr), 0);
+        assert_eq!(
+            libc::pthread_attr_settee(
+                &mut attr,
+                libc::TEESMP_THREAD_ATTR_CA_INHERIT,
+                libc::TEESMP_THREAD_ATTR_TASK_ID_INHERIT,
+                libc::TEESMP_THREAD_ATTR_HAS_SHADOW,
+            ),
+            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);
+            }
+        };
+
+        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
+        // be leaked. However, an alternative design could cause double-free
+        // which is clearly worse.
+        assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+
+        return if ret != 0 {
+            // The thread failed to start and as a result p was not consumed. Therefore, it is
+            // safe to reconstruct the box so that it gets deallocated.
+            drop(Box::from_raw(p));
+            Err(io::Error::from_raw_os_error(ret))
+        } else {
+            // The new thread will start running earliest after the next yield.
+            // We add a yield here, so that the user does not have to.
+            Thread::yield_now();
+            Ok(Thread { id: native })
+        };
+
+        extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void {
+            unsafe {
+                // Next, set up our stack overflow handler which may get triggered if we run
+                // out of stack.
+                // this is not necessary in TEE.
+                //let _handler = stack_overflow::Handler::new();
+                // Finally, let's run some code.
+                Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+            }
+            ptr::null_mut()
+        }
+    }
+
+    pub fn yield_now() {
+        let ret = unsafe { libc::sched_yield() };
+        debug_assert_eq!(ret, 0);
+    }
+
+    /// This does not do anything on teeos
+    pub fn set_name(_name: &CStr) {
+        // Both pthread_setname_np and prctl are not available to the TA,
+        // so we can't implement this currently. If the need arises please
+        // contact the teeos rustzone team.
+    }
+
+    /// only main thread could wait for sometime in teeos
+    pub fn sleep(dur: Duration) {
+        let sleep_millis = dur.as_millis();
+        let final_sleep: u32 =
+            if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 };
+        unsafe {
+            let _ = TEE_Wait(final_sleep);
+        }
+    }
+
+    /// must join, because no pthread_detach supported
+    pub fn join(self) {
+        unsafe {
+            let ret = libc::pthread_join(self.id, ptr::null_mut());
+            mem::forget(self);
+            assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret));
+        }
+    }
+
+    pub fn id(&self) -> libc::pthread_t {
+        self.id
+    }
+
+    pub fn into_id(self) -> libc::pthread_t {
+        let id = self.id;
+        mem::forget(self);
+        id
+    }
+}
+
+impl Drop for Thread {
+    fn drop(&mut self) {
+        // we can not call detach, so just panic if thread spawn without join
+        panic!("thread must join, detach is not supported!");
+    }
+}
+
+// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on
+// teeos, so this function always returns an Error!
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+    Err(io::Error::new(
+        io::ErrorKind::NotFound,
+        "The number of hardware threads is not known for the target platform",
+    ))
+}
+
+// stub
+pub mod guard {
+    use crate::ops::Range;
+    pub type Guard = Range<usize>;
+    pub unsafe fn current() -> Option<Guard> {
+        None
+    }
+    pub unsafe fn init() -> Option<Guard> {
+        None
+    }
+}
+
+fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
+    libc::PTHREAD_STACK_MIN.try_into().expect("Infallible")
+}
diff --git a/library/std/src/sys/teeos/thread_local_dtor.rs b/library/std/src/sys/teeos/thread_local_dtor.rs
new file mode 100644
index 00000000000..5c6bc4d6750
--- /dev/null
+++ b/library/std/src/sys/teeos/thread_local_dtor.rs
@@ -0,0 +1,4 @@
+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);
+}
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index f2e86a4fb2b..f62eb828ee5 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -23,11 +23,11 @@ struct Nanoseconds(u32);
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SystemTime {
-    pub(in crate::sys::unix) t: Timespec,
+    pub(crate) t: Timespec,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(in crate::sys::unix) struct Timespec {
+pub(crate) struct Timespec {
     tv_sec: i64,
     tv_nsec: Nanoseconds,
 }
@@ -239,11 +239,11 @@ impl From<libc::timespec> for Timespec {
     not(target_arch = "riscv32")
 ))]
 #[repr(C)]
-pub(in crate::sys::unix) struct __timespec64 {
-    pub(in crate::sys::unix) tv_sec: i64,
+pub(crate) struct __timespec64 {
+    pub(crate) tv_sec: i64,
     #[cfg(target_endian = "big")]
     _padding: i32,
-    pub(in crate::sys::unix) tv_nsec: i32,
+    pub(crate) tv_nsec: i32,
     #[cfg(target_endian = "little")]
     _padding: i32,
 }
@@ -255,7 +255,7 @@ pub(in crate::sys::unix) struct __timespec64 {
     not(target_arch = "riscv32")
 ))]
 impl __timespec64 {
-    pub(in crate::sys::unix) fn new(tv_sec: i64, tv_nsec: i32) -> Self {
+    pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self {
         Self { tv_sec, tv_nsec, _padding: 0 }
     }
 }
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 4b9ddd5aba1..8498937809e 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -1581,6 +1581,7 @@ impl<'scope, T> JoinInner<'scope, T> {
 /// [`thread::Builder::spawn`]: Builder::spawn
 /// [`thread::spawn`]: spawn
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(target_os = "teeos", must_use)]
 pub struct JoinHandle<T>(JoinInner<'static, T>);
 
 #[stable(feature = "joinhandle_impl_send_sync", since = "1.29.0")]
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md
index 9233a36db3d..7a6609b2d76 100644
--- a/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-teeos.md
@@ -39,7 +39,7 @@ Create the following shell scripts that wrap Clang from the OpenHarmony SDK:
 ```sh
 #!/bin/sh
 exec /path/to/ohos-sdk/linux/native/llvm/bin/clang \
-  --target aarch64-linux-gnu \
+  -target aarch64-linux-gnu \
   "$@"
 ```
 
@@ -48,7 +48,7 @@ exec /path/to/ohos-sdk/linux/native/llvm/bin/clang \
 ```sh
 #!/bin/sh
 exec /path/to/ohos-sdk/linux/native/llvm/bin/clang++ \
-  --target aarch64-linux-gnu \
+  -target aarch64-linux-gnu \
   "$@"
 ```
 
@@ -81,6 +81,13 @@ ranlib = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ranlib"
 llvm-config = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-config"
 ```
 
+```text
+note: You need to insert "/usr/include/x86_64-linux-gnu/" into environment variable: $C_INCLUDE_PATH
+ if some header files like bits/xxx.h not found.
+note: You can install gcc-aarch64-linux-gnu,g++-aarch64-linux-gnu if some files like crti.o not found.
+note: You may need to install libc6-dev-i386 libc6-dev if "gnu/stubs-32.h" not found.
+```
+
 ## Building Rust programs
 
 Rust does not yet ship pre-compiled artifacts for this target. To compile for
@@ -91,7 +98,7 @@ this target, you will either need to build Rust with the target enabled (see
 You will need to configure the linker to use in `~/.cargo/config`:
 ```toml
 [target.aarch64-unknown-teeos]
-linker = "/path/to/aarch64-unknown-teeos-clang.sh"
+linker = "/path/to/aarch64-unknown-teeos-clang.sh" # or aarch64-linux-gnu-ld
 ```
 
 ## Testing