From 03f6362756688207b244ad17a6d0d3a2864ded8a Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 19 Nov 2024 00:09:41 +0200 Subject: [PATCH] libc: properly set up main thread --- test.c | 1 + userspace/lib/ygglibc/build.rs | 3 - .../lib/ygglibc/src/headers/fcntl/mod.rs | 1 - userspace/lib/ygglibc/src/headers/mod.rs | 5 +- .../src/headers/sys_yggdrasil/cbindgen.toml | 12 ++++ .../ygglibc/src/headers/sys_yggdrasil/mod.rs | 14 +++++ userspace/lib/ygglibc/src/lib.rs | 3 +- userspace/lib/ygglibc/src/panic.rs | 21 +++++-- userspace/lib/ygglibc/src/thread/mod.rs | 56 ++++++++++++++----- xtask/src/build/c.rs | 10 +--- xtask/src/build/toolchain_c.rs | 44 ++++++++------- xtask/src/error.rs | 38 ++++++++++++- xtask/src/main.rs | 1 + 13 files changed, 156 insertions(+), 53 deletions(-) create mode 100644 userspace/lib/ygglibc/src/headers/sys_yggdrasil/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/sys_yggdrasil/mod.rs diff --git a/test.c b/test.c index a0604951..eccebfcd 100644 --- a/test.c +++ b/test.c @@ -1,6 +1,7 @@ #include #include #include +#include _Thread_local int x = 123; diff --git a/userspace/lib/ygglibc/build.rs b/userspace/lib/ygglibc/build.rs index 5617538c..9c040947 100644 --- a/userspace/lib/ygglibc/build.rs +++ b/userspace/lib/ygglibc/build.rs @@ -193,9 +193,6 @@ fn main() { .map(|d| d.path().as_path().join("cbindgen.toml")) .filter(|p| p.exists()) .for_each(|p| { - println!("cargo:rerun-if-changed={:?}", p.parent().unwrap()); - println!("cargo:rerun-if-changed={:?}", p); - println!("cargo:rerun-if-changed={:?}", p.with_file_name("mod.rs")); generate_header(&p, &header_output); }); } diff --git a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs index 74e57b6d..07271ba4 100644 --- a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs +++ b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs @@ -164,7 +164,6 @@ unsafe extern "C" fn creat(pathname: *const c_char, mode: mode_t) -> CFdResult { #[no_mangle] unsafe extern "C" fn open(pathname: *const c_char, opts: c_int, mut args: ...) -> CFdResult { - yggdrasil_rt::debug_trace!("&errno = {:p}", &crate::error::errno); vopenat(AT_FDCWD, pathname, opts, args.as_va_list()) } diff --git a/userspace/lib/ygglibc/src/headers/mod.rs b/userspace/lib/ygglibc/src/headers/mod.rs index c6146491..8675b7f8 100644 --- a/userspace/lib/ygglibc/src/headers/mod.rs +++ b/userspace/lib/ygglibc/src/headers/mod.rs @@ -122,6 +122,8 @@ pub mod utime; pub mod wchar; pub mod wctype; +pub mod pthread; + pub mod sys_mman; pub mod sys_resource; pub mod sys_select; @@ -135,4 +137,5 @@ pub mod sys_wait; // TODO Generate those as part of dyn-loader (and make dyn-loader a shared library) pub mod link; -pub mod pthread; + +pub mod sys_yggdrasil; diff --git a/userspace/lib/ygglibc/src/headers/sys_yggdrasil/cbindgen.toml b/userspace/lib/ygglibc/src/headers/sys_yggdrasil/cbindgen.toml new file mode 100644 index 00000000..03987a82 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/sys_yggdrasil/cbindgen.toml @@ -0,0 +1,12 @@ +language = "C" +style = "Type" + +sys_includes = [ + "stddef.h", +] +no_includes = true + +include_guard = "_SYS_YGGDRASIL_H" + +usize_type = "size_t" +isize_type = "ssize_t" diff --git a/userspace/lib/ygglibc/src/headers/sys_yggdrasil/mod.rs b/userspace/lib/ygglibc/src/headers/sys_yggdrasil/mod.rs new file mode 100644 index 00000000..00678a9c --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/sys_yggdrasil/mod.rs @@ -0,0 +1,14 @@ +use core::ffi::{c_char, CStr}; + +#[no_mangle] +unsafe extern "C" fn ygg_panic(message: *const c_char) -> ! { + let message = if message.is_null() { + "" + } else if let Ok(str) = CStr::from_ptr(message).to_str() { + str + } else { + "" + }; + + panic!("Explicit panic: {message}") +} diff --git a/userspace/lib/ygglibc/src/lib.rs b/userspace/lib/ygglibc/src/lib.rs index bc2f8ca5..14aed4b1 100644 --- a/userspace/lib/ygglibc/src/lib.rs +++ b/userspace/lib/ygglibc/src/lib.rs @@ -11,7 +11,8 @@ rustc_private, naked_functions, non_null_from_ref, - thread_local + thread_local, + let_chains )] #![allow(internal_features)] #![cfg_attr(not(test), no_std)] diff --git a/userspace/lib/ygglibc/src/panic.rs b/userspace/lib/ygglibc/src/panic.rs index 920f5d3a..b370bc40 100644 --- a/userspace/lib/ygglibc/src/panic.rs +++ b/userspace/lib/ygglibc/src/panic.rs @@ -1,12 +1,15 @@ use core::{fmt, sync::atomic::AtomicUsize}; -use crate::io::{managed::{stderr, FILE}, Write}; +use crate::io::{ + managed::{stderr, FILE}, + Write, +}; struct PanicPrinter { buffer: [u8; 1024], pos: usize, - print: Option<&'static mut FILE> + print: Option<&'static mut FILE>, } impl fmt::Write for PanicPrinter { @@ -59,6 +62,7 @@ impl PanicPrinter { } } +#[thread_local] static PANIC_COUNT: AtomicUsize = AtomicUsize::new(0); #[cfg(any(not(test), rust_analyzer))] @@ -66,11 +70,21 @@ static PANIC_COUNT: AtomicUsize = AtomicUsize::new(0); fn panic_handler(pi: &core::panic::PanicInfo) -> ! { use core::{fmt::Write, sync::atomic::Ordering}; + use crate::{error::EResult, thread::Thread}; + match PANIC_COUNT.fetch_add(1, Ordering::Relaxed) { 0 => { + let pthread_self = Thread::this(); let mut printer = PanicPrinter::new(); writeln!(printer, "!!! ygglibc panic !!!").ok(); + if let EResult::Ok(this) = pthread_self { + if this.is_main() { + writeln!(printer, "* Main thread panicked *").ok(); + } else { + writeln!(printer, "* Thread {} panicked*", this.id()).ok(); + } + } if let Some(location) = pi.location() { writeln!(printer, "{}:{}:", location.file(), location.line()).ok(); } @@ -84,6 +98,5 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! { _ => {} } - // Kill myself - super::process::abort(); + Thread::abort_current() } diff --git a/userspace/lib/ygglibc/src/thread/mod.rs b/userspace/lib/ygglibc/src/thread/mod.rs index 5641fc64..40bfc9e3 100644 --- a/userspace/lib/ygglibc/src/thread/mod.rs +++ b/userspace/lib/ygglibc/src/thread/mod.rs @@ -1,4 +1,5 @@ use core::{ + cell::OnceCell, ffi::c_void, mem, pin::Pin, @@ -13,9 +14,10 @@ use yggdrasil_rt::{ }; use crate::{ - error::EResult, - headers::sys_types::{pthread_attr_t, pthread_t}, - sync::Mutex, + error::{EResult, OptionExt}, headers::{ + errno::{self, Errno}, + sys_types::{pthread_attr_t, pthread_t}, + }, process, sync::Mutex }; pub mod tls; @@ -23,7 +25,7 @@ pub mod tls; static THREADS: Mutex>> = Mutex::new(BTreeMap::new()); #[thread_local] -static mut SELF: Option> = None; +static SELF: OnceCell> = OnceCell::new(); enum ThreadStack { Owned(usize, usize), @@ -44,6 +46,18 @@ pub struct Thread { } impl Thread { + fn main() -> Self { + Self { + id: AtomicU32::new(0), + stack: ThreadStack::Borrowed, + result: AtomicPtr::new(null_mut()) + } + } + + pub fn is_main(&self) -> bool { + self.id.load(Ordering::Acquire) == 0 + } + pub fn spawn( attr: Option<&pthread_attr_t>, entry: extern "C" fn(*mut c_void) -> *mut c_void, @@ -117,28 +131,38 @@ impl Thread { } pub fn this() -> EResult> { - let ptr = unsafe { SELF.clone() }.expect("pthread_self() is NULL"); - EResult::Ok(ptr) + SELF.get().cloned().e_ok_or(errno::EINVAL) } unsafe fn set_this(thread: Arc) { - unsafe { SELF = Some(thread) }; + if SELF.set(thread).is_err() { + unreachable!("pthread_self already initialized?") + } } unsafe fn clear_this() { - unsafe { SELF = None }; + let this = SELF.get().expect("pthread_self == NULL"); + Arc::decrement_strong_count(this); } pub fn result(&self) -> *mut c_void { self.result.load(Ordering::Acquire) } + pub fn abort_current() -> ! { + let this = Self::this(); + if let EResult::Ok(this) = this && !this.is_main() { + this.result.store(null_mut(), Ordering::Release); + unsafe { yggdrasil_rt::sys::exit_thread() }; + } else { + process::abort(); + } + } + extern "C" fn thread_entry(argument: usize) -> ! { // Set up TLS as soon as possible. Note the `force = true` parameter, because the image // contains "already initialized" tag, which only matters for the main thread. - if let Err(err) = - unsafe { thread_local::init_tls(tls::TLS_IMAGE.as_ref(), true) } - { + if let Err(err) = unsafe { thread_local::init_tls(tls::TLS_IMAGE.as_ref(), true) } { yggdrasil_rt::debug_trace!("thread_entry failed: TLS init error: {err:?}"); unsafe { yggdrasil_rt::sys::exit_thread() }; } @@ -187,11 +211,17 @@ pub fn init_main_thread(arg: &ProgramArgumentInner) { // Usually, a dynamic loader will do this for us, but this still needs to be // done for statically-linked programs if the kernel transfers control directly to // the program. - let tls_image = thread_local::init_tls_from_auxv(arg.auxv(), false) - .expect("Could not initialize TLS"); + let tls_image = + thread_local::init_tls_from_auxv(arg.auxv(), false).expect("Could not initialize TLS"); // Store the TLS image, it'll be needed when creating new threads unsafe { tls::TLS_IMAGE = tls_image; } + + // TODO set thread ID for main + let main = Arc::new(Thread::main()); + unsafe { + Thread::set_this(main); + } } diff --git a/xtask/src/build/c.rs b/xtask/src/build/c.rs index 219fd359..c7bdbc5d 100644 --- a/xtask/src/build/c.rs +++ b/xtask/src/build/c.rs @@ -41,7 +41,6 @@ fn build_test_cpp_program( let mut command = llvm.c_clangpp(env); command .args([ - "-v", "-fpie", "-Bdynamic", "-O0", @@ -93,14 +92,7 @@ fn build_test_c_program( log::info!("Building a test C program [static]"); let mut command = llvm.c_clang(env); command - .args([ - "-v", - "-static", - "-O0", - "-ggdb", - "-fstack-protector-strong", - "-lm", - ]) + .args(["-static", "-O0", "-ggdb", "-fstack-protector-strong", "-lm"]) .arg("-o") .arg(target_dir.join("c-test-static")) .arg(env.workspace_root.join("test.c")); diff --git a/xtask/src/build/toolchain_c.rs b/xtask/src/build/toolchain_c.rs index b35cb011..3e3fdcf1 100644 --- a/xtask/src/build/toolchain_c.rs +++ b/xtask/src/build/toolchain_c.rs @@ -1,6 +1,13 @@ -use std::{fs, path::PathBuf, process::Command}; +use std::{ + fs, + path::PathBuf, + process::{Command, Stdio}, +}; -use crate::{env::BuildEnv, error::Error}; +use crate::{ + env::BuildEnv, + error::{CommandExt, Error}, +}; pub struct Llvm { root: PathBuf, @@ -76,23 +83,22 @@ pub fn install_compiler_rt(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error> { .arg("-GNinja") .arg("../../compiler-rt"); - if !command.status()?.success() { - todo!() - } + command.success().map_err(Error::CompilerRtConfigFailed)?; } // Build and install compiler-rt log::info!("Build compiler-rt for {}", env.arch.name()); let mut command = Command::new("cmake"); + if !env.verbose { + command.stdout(Stdio::null()); + } command.current_dir(&build_dir); command .args(["--build", "."]) .args(["-t install"]) .arg("-j"); - if !command.status()?.success() { - todo!() - } + command.success().map_err(Error::CompilerRtBuildFailed)?; Ok(()) } @@ -149,9 +155,7 @@ pub fn install_llvm_cxx_runtime(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error )) .arg("../../runtimes"); - if !command.status()?.success() { - todo!() - } + command.success().map_err(Error::CRuntimeConfigFailed)?; } // Build and install libc++/abi @@ -160,15 +164,16 @@ pub fn install_llvm_cxx_runtime(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error // present in the sysroot log::info!("Build libc++/libc++abi for {}", env.arch.name()); let mut command = Command::new("cmake"); + if !env.verbose { + command.stdout(Stdio::null()); + } command.current_dir(&build_dir); command .args(["--build", "."]) .args(["-t install"]) .arg("-j"); - if !command.status()?.success() { - todo!() - } + command.success().map_err(Error::CRuntimeBuildFailed)?; Ok(()) } @@ -203,23 +208,22 @@ pub fn install_llvm_compiler(env: &BuildEnv) -> Result { .arg("-GNinja") .arg("../llvm"); - if !command.status()?.success() { - todo!() - } + command.success().map_err(Error::LlvmConfigFailed)?; } // Build and install LLVM log::info!("Build LLVM"); let mut command = Command::new("cmake"); + if !env.verbose { + command.stdout(Stdio::null()); + } command.current_dir(&build_dir); command .args(["--build", "."]) .args(["-t install"]) .arg("-j"); - if !command.status()?.success() { - todo!() - } + command.success().map_err(Error::LlvmBuildFailed)?; Ok(Llvm { root }) } diff --git a/xtask/src/error.rs b/xtask/src/error.rs index 0074e24d..f2685386 100644 --- a/xtask/src/error.rs +++ b/xtask/src/error.rs @@ -1,4 +1,8 @@ -use std::{io, path::PathBuf}; +use std::{ + io, + path::PathBuf, + process::{Command, ExitStatusError}, +}; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -30,4 +34,36 @@ pub enum Error { InvalidPath(PathBuf), #[error("Could not resolve modules")] UnresolvedModuleDependencies, + + #[error("LLVM config failed: {0}")] + LlvmConfigFailed(CommandFailed), + #[error("LLVM build failed: {0}")] + LlvmBuildFailed(CommandFailed), + #[error("C/C++ runtime config failed: {0}")] + CRuntimeConfigFailed(CommandFailed), + #[error("C/C++ runtime build failed: {0}")] + CRuntimeBuildFailed(CommandFailed), + #[error("compiler-rt config failed: {0}")] + CompilerRtConfigFailed(CommandFailed), + #[error("compiler-rt build failed: {0}")] + CompilerRtBuildFailed(CommandFailed), +} + +#[derive(Debug, thiserror::Error)] +pub enum CommandFailed { + #[error("{0}")] + Io(#[from] io::Error), + #[error("exit status {0:?}")] + ExitCode(#[from] ExitStatusError), +} + +pub trait CommandExt { + fn success(self) -> Result<(), CommandFailed>; +} + +impl CommandExt for Command { + fn success(mut self) -> Result<(), CommandFailed> { + self.status()?.exit_ok()?; + Ok(()) + } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ee2b2010..2f8e853c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,4 +1,5 @@ #![deny(warnings)] +#![feature(exit_status_error)] use std::{fs, path::PathBuf, process::ExitCode};