From 588e9e2936ea5311267c4dfd6015a48653b88bc1 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Tue, 26 Nov 2024 22:59:23 +0200 Subject: [PATCH] libc: implement threads for libcxx --- kernel/libk/src/task/futex.rs | 11 +- kernel/src/arch/aarch64/exception.rs | 25 ++- kernel/src/syscall/imp/sys_process.rs | 8 +- lib/abi/src/process/mod.rs | 6 +- lib/runtime/src/process/thread_local/mod.rs | 25 ++- lib/runtime/src/sync/mutex.rs | 2 +- lib/runtime/src/sync/once.rs | 2 +- lib/runtime/src/sync/rwlock.rs | 2 +- test.c | 31 +-- test.cpp | 22 +-- toolchain-c/cmake/toolchain-aarch64.cmake | 2 +- toolchain-c/cmake/toolchain-i686.cmake | 2 +- toolchain-c/cmake/toolchain-x86_64.cmake | 2 +- userspace/dyn-loader/src/thread_local/mod.rs | 2 +- userspace/lib/ygglibc/build.rs | 1 + userspace/lib/ygglibc/include/bits/pthread.h | 6 +- .../ygglibc/src/headers/pthread/barrier.rs | 2 +- .../lib/ygglibc/src/headers/pthread/cond.rs | 181 ++++++++++++++++-- .../lib/ygglibc/src/headers/pthread/mod.rs | 4 +- .../lib/ygglibc/src/headers/pthread/mutex.rs | 6 +- .../lib/ygglibc/src/headers/pthread/once.rs | 27 +++ .../lib/ygglibc/src/headers/pthread/tls.rs | 53 ++++- .../src/headers/sys_types/cbindgen.toml | 7 +- .../lib/ygglibc/src/headers/sys_types/mod.rs | 13 +- .../ygglibc/src/headers/time/cbindgen.toml | 2 +- userspace/lib/ygglibc/src/init.rs | 43 ++++- userspace/lib/ygglibc/src/lib.rs | 1 + userspace/lib/ygglibc/src/panic.rs | 12 +- xtask/src/build/c.rs | 16 ++ xtask/src/build/toolchain_c.rs | 6 +- 30 files changed, 427 insertions(+), 95 deletions(-) create mode 100644 userspace/lib/ygglibc/src/headers/pthread/once.rs diff --git a/kernel/libk/src/task/futex.rs b/kernel/libk/src/task/futex.rs index 98cf63de..de59b709 100644 --- a/kernel/libk/src/task/futex.rs +++ b/kernel/libk/src/task/futex.rs @@ -64,14 +64,9 @@ impl UserspaceMutex { // self.wait_predicate(|value| value != compare_value).await // } - /// Wakes up a single task waiting on the mutex - pub fn wake(&self) { - self.queue.wake_one(); - } - - /// Wakes up all tasks waiting on the mutex - pub fn wake_all(&self) { - self.queue.wake_all(); + /// Wakes up at most `limit` threads waiting on this mutex + pub fn wake(&self, limit: usize) { + self.queue.wake_some(limit); } } diff --git a/kernel/src/arch/aarch64/exception.rs b/kernel/src/arch/aarch64/exception.rs index b802e123..4ce1834e 100644 --- a/kernel/src/arch/aarch64/exception.rs +++ b/kernel/src/arch/aarch64/exception.rs @@ -5,8 +5,8 @@ use core::arch::global_asm; use aarch64_cpu::{ asm::barrier, registers::{ - ELR_EL1, ESR_EL1, FAR_EL1, SCTLR_EL1, TCR_EL1, TPIDR_EL0, TPIDR_EL1, TTBR0_EL1, TTBR1_EL1, - VBAR_EL1, + ELR_EL1, ESR_EL1, FAR_EL1, SCTLR_EL1, SP_EL0, TCR_EL1, TPIDR_EL0, TPIDR_EL1, TTBR0_EL1, + TTBR1_EL1, VBAR_EL1, }, }; use abi::{process::Signal, SyscallFunction}; @@ -17,6 +17,7 @@ use tock_registers::interfaces::{Readable, Writeable}; use crate::{ debug::LogLevel, + mem::KERNEL_VIRT_OFFSET, syscall::{self, raw_syscall_handler}, }; @@ -33,12 +34,15 @@ pub fn init_exceptions() { fn dump_irrecoverable_exception(frame: &ExceptionFrame, ec: u64, iss: u64) { let cpu = Cpu::try_local(); + let far = FAR_EL1.get() as usize; + let ttbr0 = TTBR0_EL1.get(); log_print_raw!(LogLevel::Fatal, "SYNC exception:\n"); - log_print_raw!(LogLevel::Fatal, "FAR: {:#x}\n", FAR_EL1.get()); + log_print_raw!(LogLevel::Fatal, "FAR: {:#x}\n", far); log_print_raw!(LogLevel::Fatal, "ELR: {:#x}\n", ELR_EL1.get()); log_print_raw!(LogLevel::Fatal, "ESR: {:#x}\n", ESR_EL1.get()); - log_print_raw!(LogLevel::Fatal, "TTBR0_EL1: {:#x}\n", TTBR0_EL1.get()); + log_print_raw!(LogLevel::Fatal, "SP_EL0: {:#x}\n", SP_EL0.get()); + log_print_raw!(LogLevel::Fatal, "TTBR0_EL1: {:#x}\n", ttbr0); log_print_raw!(LogLevel::Fatal, "TTBR1_EL1: {:#x}\n", TTBR1_EL1.get()); log_print_raw!(LogLevel::Fatal, "Register dump:\n"); log_print_raw!(LogLevel::Fatal, "{:?}\n", frame); @@ -49,6 +53,19 @@ fn dump_irrecoverable_exception(frame: &ExceptionFrame, ec: u64, iss: u64) { if let Some(current) = current { log_print_raw!(LogLevel::Fatal, "In thread {}\n", current.id); + + let space = current.address_space(); + if far < KERNEL_VIRT_OFFSET && space.as_address_with_asid() == ttbr0 { + match space.translate(far) { + Ok(phys) => log_print_raw!( + LogLevel::Fatal, + "FAR translation: {:#x} -> {:#x}\n", + far, + phys + ), + Err(_) => log_print_raw!(LogLevel::Fatal, "FAR does not translate\n"), + } + } } } diff --git a/kernel/src/syscall/imp/sys_process.rs b/kernel/src/syscall/imp/sys_process.rs index 328ef28c..bd09b10d 100644 --- a/kernel/src/syscall/imp/sys_process.rs +++ b/kernel/src/syscall/imp/sys_process.rs @@ -252,12 +252,8 @@ pub(crate) fn mutex(mutex: &AtomicU32, op: &MutexOperation) -> Result<(), Error> &MutexOperation::WaitUntilSet(mask, _timeout) => { block! { mutex.wait_until(|v| v & mask == v).await }? } - MutexOperation::Wake => { - mutex.wake(); - Ok(()) - } - MutexOperation::WakeAll => { - mutex.wake_all(); + MutexOperation::Wake(limit) => { + mutex.wake(*limit as _); Ok(()) } } diff --git a/lib/abi/src/process/mod.rs b/lib/abi/src/process/mod.rs index c1d18159..eadb5641 100644 --- a/lib/abi/src/process/mod.rs +++ b/lib/abi/src/process/mod.rs @@ -60,10 +60,8 @@ pub enum MutexOperation { WaitWhileSet(u32, Option), /// Waits on the mutex object until `mutex & A != 0` WaitUntilSet(u32, Option), - /// Wakes a single mutex-waiting thread - Wake, - /// Wakes all threads waiting on the mutex - WakeAll, + /// Wakes at most N mutex-waiting threads + Wake(u32), } /// Provides some amount of process-related information diff --git a/lib/runtime/src/process/thread_local/mod.rs b/lib/runtime/src/process/thread_local/mod.rs index af87fdab..9e4ca6f6 100644 --- a/lib/runtime/src/process/thread_local/mod.rs +++ b/lib/runtime/src/process/thread_local/mod.rs @@ -142,11 +142,12 @@ impl Dtv { } #[inline] - fn set_key(list: &mut [*mut c_void], key: usize, value: *mut c_void) { + fn set_key(list: &mut [*mut c_void], key: usize, value: *mut c_void) -> bool { if key == 0 || key > list.len() { - panic!("Out-of-bounds TLS key: {key}") + return false; } list[key - 1] = value; + true } /// Returns a value assocaited with a given thread-specific key. @@ -166,10 +167,24 @@ impl Dtv { /// Will panic if key == 0. /// Will panic if key is longer than the DTV itself. pub fn set_specific(&mut self, key: usize, value: *mut c_void, grow: bool) { + self.try_set_specific(key, value, grow) + .expect("Dtv::set_specific(): invalid key") + } + + /// Sets a DTV entry for a thread-specific key. + pub fn try_set_specific( + &mut self, + key: usize, + value: *mut c_void, + grow: bool, + ) -> Result<(), Error> { if key > self.entries.len() && grow { self.specific.resize(key, null_mut()); } - Self::set_key(&mut self.specific, key, value) + if !Self::set_key(&mut self.specific, key, value) { + return Err(Error::InvalidArgument); + } + Ok(()) } /// Allocates a new thread-specific key in the DTV, filling it with a NULL value. @@ -197,7 +212,9 @@ impl Dtv { if key > self.entries.len() { self.entries.resize(key, null_mut()); } - Self::set_key(&mut self.entries, key, value) + if !Self::set_key(&mut self.entries, key, value) { + panic!("Dtv::set(): invalid key"); + } } } diff --git a/lib/runtime/src/sync/mutex.rs b/lib/runtime/src/sync/mutex.rs index 1ce32597..3fdb5357 100644 --- a/lib/runtime/src/sync/mutex.rs +++ b/lib/runtime/src/sync/mutex.rs @@ -103,7 +103,7 @@ impl RawMutex { } fn notify_one(&self) { - unsafe { crate::sys::mutex(&self.state, &MutexOperation::Wake).ok() }; + unsafe { crate::sys::mutex(&self.state, &MutexOperation::Wake(1)).ok() }; } } diff --git a/lib/runtime/src/sync/once.rs b/lib/runtime/src/sync/once.rs index 4ad876f5..bee3be12 100644 --- a/lib/runtime/src/sync/once.rs +++ b/lib/runtime/src/sync/once.rs @@ -107,7 +107,7 @@ impl Once { } fn notify(&self) { - unsafe { crate::sys::mutex(&self.state, &MutexOperation::WakeAll) }.ok(); + unsafe { crate::sys::mutex(&self.state, &MutexOperation::Wake(u32::MAX)) }.ok(); } fn wait_initialized(&self) { diff --git a/lib/runtime/src/sync/rwlock.rs b/lib/runtime/src/sync/rwlock.rs index 00e684d0..3fad8c25 100644 --- a/lib/runtime/src/sync/rwlock.rs +++ b/lib/runtime/src/sync/rwlock.rs @@ -142,7 +142,7 @@ impl RwLock { fn notify_readable(&self) {} fn notify_writeable(&self) { - unsafe { crate::sys::mutex(&self.state, &MutexOperation::Wake) }.ok(); + unsafe { crate::sys::mutex(&self.state, &MutexOperation::Wake(1)) }.ok(); } } diff --git a/test.c b/test.c index 215f522d..85c24f68 100644 --- a/test.c +++ b/test.c @@ -3,24 +3,33 @@ #include #include +static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + static void *thread(void *arg) { - pthread_t self = pthread_self(); - printf("[child] pthread_self() = %u\n", self); - sleep(3); - return arg; + const char *name = (const char *) arg; + printf("[%s] wait\n", name); + pthread_mutex_lock(&mutex); + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + printf("[%s] done\n", name); + return NULL; } int main(int argc, const char **argv) { - pthread_t id; + pthread_t handle0, handle1; + pthread_create(&handle0, NULL, thread, "thread0"); + pthread_create(&handle1, NULL, thread, "thread1"); - assert(pthread_create(&id, NULL, thread, (void *) 0x1234) == 0); + sleep(3); - pthread_t self = pthread_self(); - printf("[main] pthread_self() = %u\n", self); + pthread_mutex_lock(&mutex); + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); - void *result; - assert(pthread_join(id, &result) == 0); - printf("[main] result: %p\n", result); + pthread_join(handle0, NULL); + pthread_join(handle1, NULL); + printf("[main] finished\n"); return 0; } diff --git a/test.cpp b/test.cpp index 230c9057..8540cf9f 100644 --- a/test.cpp +++ b/test.cpp @@ -1,21 +1,21 @@ #include #include #include +#include int main() { - std::vector items; - items.push_back(1); - items.push_back(2); - items.push_back(3); + std::cout << "Spawning a thread" << std::endl; + std::cout << "Main thread is: " << std::this_thread::get_id() << std::endl; + std::cout << "Hardware concurrency: " << std::thread::hardware_concurrency() << std::endl; - for (const auto &item: items) { - std::cout << "Item: " << item << std::endl; - } + std::thread t1([]() { + std::cout << "Hello from a thread!!!" << std::endl; + std::cout << "This is thread: " << std::this_thread::get_id() << std::endl; + }); - std::cout << "&errno = " << &errno << std::endl; - std::cout << "errno = " << errno << std::endl; - open("/xxxsaa", 0, 0); - std::cout << "errno = " << errno << std::endl; + std::cout << "Waiting for a thread" << std::endl; + t1.join(); + std::cout << "Thread finished" << std::endl; return 0; } diff --git a/toolchain-c/cmake/toolchain-aarch64.cmake b/toolchain-c/cmake/toolchain-aarch64.cmake index ba5457d9..79592d4e 100644 --- a/toolchain-c/cmake/toolchain-aarch64.cmake +++ b/toolchain-c/cmake/toolchain-aarch64.cmake @@ -17,6 +17,6 @@ set(CMAKE_ASM_COMPILER_TARGET "aarch64-unknown-yggdrasil") # set(CMAKE_SHARED_LINKER_FLAGS "-v") set(CMAKE_C_FLAGS "--target=aarch64-unknown-yggdrasil -fPIC") -set(CMAKE_CXX_FLAGS "--target=aarch64-unknown-yggdrasil -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK=1 -D_LIBCPP_HAS_NO_TREE_BARRIER") +set(CMAKE_CXX_FLAGS "--target=aarch64-unknown-yggdrasil -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_TREE_BARRIER") # Specify any additional include paths or linker flags as needed diff --git a/toolchain-c/cmake/toolchain-i686.cmake b/toolchain-c/cmake/toolchain-i686.cmake index 262d8735..9b435ad7 100644 --- a/toolchain-c/cmake/toolchain-i686.cmake +++ b/toolchain-c/cmake/toolchain-i686.cmake @@ -17,6 +17,6 @@ set(CMAKE_ASM_COMPILER_TARGET "i686-unknown-yggdrasil") # set(CMAKE_SHARED_LINKER_FLAGS "-v") set(CMAKE_C_FLAGS "--target=i686-unknown-yggdrasil -fPIC -m32") -set(CMAKE_CXX_FLAGS "--target=i686-unknown-yggdrasil -m32 -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK=1 -D_LIBCPP_HAS_NO_TREE_BARRIER") +set(CMAKE_CXX_FLAGS "--target=i686-unknown-yggdrasil -m32 -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_TREE_BARRIER") # Specify any additional include paths or linker flags as needed diff --git a/toolchain-c/cmake/toolchain-x86_64.cmake b/toolchain-c/cmake/toolchain-x86_64.cmake index 96593ac1..efafda37 100644 --- a/toolchain-c/cmake/toolchain-x86_64.cmake +++ b/toolchain-c/cmake/toolchain-x86_64.cmake @@ -17,4 +17,4 @@ set(CMAKE_ASM_COMPILER_TARGET "x86_64-unknown-yggdrasil") # set(CMAKE_SHARED_LINKER_FLAGS "-v") set(CMAKE_C_FLAGS "--target=x86_64-unknown-yggdrasil -fPIC") -set(CMAKE_CXX_FLAGS "--target=x86_64-unknown-yggdrasil -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_MONOTONIC_CLOCK=1 -D_LIBCPP_HAS_NO_TREE_BARRIER") +set(CMAKE_CXX_FLAGS "--target=x86_64-unknown-yggdrasil -nostdlib++ -fPIC -D_LIBCPP_HAS_NO_TREE_BARRIER") diff --git a/userspace/dyn-loader/src/thread_local/mod.rs b/userspace/dyn-loader/src/thread_local/mod.rs index 8f2ec4c4..1968c5c5 100644 --- a/userspace/dyn-loader/src/thread_local/mod.rs +++ b/userspace/dyn-loader/src/thread_local/mod.rs @@ -62,7 +62,7 @@ pub fn build_tls_image( }; // Create a master image based on this layout - let mut image_data = Mapping::new(layout.total_size, MappingFlags::empty())?; + let mut image_data = Mapping::new(layout.total_size, MappingFlags::WRITE)?; let tp_offset = layout.tp_offset; diff --git a/userspace/lib/ygglibc/build.rs b/userspace/lib/ygglibc/build.rs index fd3a863f..4b86ff78 100644 --- a/userspace/lib/ygglibc/build.rs +++ b/userspace/lib/ygglibc/build.rs @@ -16,6 +16,7 @@ const RENAMES: &[(&str, &str)] = &[ ("COffsetResult", "off_t"), ("CPidResult", "pid_t"), ("AtomicU32", "uint32_t"), + ("AtomicBool", "bool"), ]; fn include_dir(d: &DirEntry) -> bool { diff --git a/userspace/lib/ygglibc/include/bits/pthread.h b/userspace/lib/ygglibc/include/bits/pthread.h index c1cc27ea..cebe85b0 100644 --- a/userspace/lib/ygglibc/include/bits/pthread.h +++ b/userspace/lib/ygglibc/include/bits/pthread.h @@ -1,6 +1,10 @@ #ifndef _YGGDRASIL_PTHREAD_H #define _YGGDRASIL_PTHREAD_H 1 -#define PTHREAD_MUTEX_INITIALIZER { 0 } +// __state +#define PTHREAD_MUTEX_INITIALIZER { 0U } + +// __read, __write +#define PTHREAD_COND_INITIALIZER { 0U, 0U } #endif diff --git a/userspace/lib/ygglibc/src/headers/pthread/barrier.rs b/userspace/lib/ygglibc/src/headers/pthread/barrier.rs index f5f564bd..5805e30b 100644 --- a/userspace/lib/ygglibc/src/headers/pthread/barrier.rs +++ b/userspace/lib/ygglibc/src/headers/pthread/barrier.rs @@ -47,7 +47,7 @@ impl pthread_barrier_t { } fn signal(&self) { - unsafe { yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::WakeAll).ok() }; + unsafe { yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::Wake(u32::MAX)).ok() }; } fn check(&self) -> bool { diff --git a/userspace/lib/ygglibc/src/headers/pthread/cond.rs b/userspace/lib/ygglibc/src/headers/pthread/cond.rs index b7f1ef82..0b9d6879 100644 --- a/userspace/lib/ygglibc/src/headers/pthread/cond.rs +++ b/userspace/lib/ygglibc/src/headers/pthread/cond.rs @@ -1,16 +1,171 @@ - // PTHREAD_COND_INITIALIZER (macro) +use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering}; -// int pthread_cond_broadcast(pthread_cond_t *); -// int pthread_cond_destroy(pthread_cond_t *); -// int pthread_cond_init(pthread_cond_t *restrict, const pthread_condattr_t *restrict); -// int pthread_cond_signal(pthread_cond_t *); -// int pthread_cond_timedwait(pthread_cond_t *restrict, pthread_mutex_t *restrict, const struct timespec *restrict); -// int pthread_cond_wait(pthread_cond_t *restrict, pthread_mutex_t *restrict); -// int pthread_condattr_destroy(pthread_condattr_t *); -// int pthread_condattr_getclock(const pthread_condattr_t *restrict, clockid_t *restrict); -// int pthread_condattr_getpshared(const pthread_condattr_t *restrict, int *restrict); -// int pthread_condattr_init(pthread_condattr_t *); -// int pthread_condattr_setclock(pthread_condattr_t *, clockid_t); -// int pthread_condattr_setpshared(pthread_condattr_t *, int); +use yggdrasil_rt::process::MutexOperation; + +use crate::{ + error::{self, CIntZeroResult, CResult, EResult}, + headers::{ + errno, + sys_time::__ygg_timespec_t, + sys_types::{clockid_t, pthread_cond_t, pthread_condattr_t, pthread_mutex_t}, + time::CLOCK_MONOTONIC, + }, + util::PointerExt, +}; + +use super::PTHREAD_PROCESS_PRIVATE; + +impl pthread_cond_t { + fn wait(&self, mutex: &pthread_mutex_t, deadline: Option<&__ygg_timespec_t>) -> EResult<()> { + // mutex is locked on entry + let value = self.__write.load(Ordering::Acquire); + self.__read.store(value, Ordering::Release); + + unsafe { mutex.release() }; + + // Wait + match deadline { + Some(_deadline) => { + todo!() + } + None => { + unsafe { + yggdrasil_rt::sys::mutex( + &self.__write, + &MutexOperation::WaitWhileEqual(value, None), + ) + } + .expect("Mutex wait failed"); + // Re-acquire the mutex + mutex.lock(); + } + } + + EResult::Ok(()) + } + + fn signal(&self, n: u32) { + // this is guarded by a mutex on entry + let value = self.__read.load(Ordering::Acquire); + self.__write.store(value.wrapping_add(1), Ordering::Release); + unsafe { yggdrasil_rt::sys::mutex(&self.__write, &MutexOperation::Wake(n)).ok() }; + } +} + +#[no_mangle] +unsafe extern "C" fn pthread_cond_init( + cond: *mut pthread_cond_t, + _attr: *const pthread_condattr_t, +) -> CIntZeroResult { + let cond = cond.ensure_mut(); + cond.__read.store(0, Ordering::Release); + cond.__write.store(0, Ordering::Release); + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_cond_destroy(_cond: *mut pthread_cond_t) -> CIntZeroResult { + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_cond_wait( + cond: *mut pthread_cond_t, + mutex: *mut pthread_mutex_t, +) -> CIntZeroResult { + let cond = cond.ensure_mut(); + let mutex = mutex.ensure_mut(); + cond.wait(mutex, None)?; + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_cond_timedwait( + cond: *mut pthread_cond_t, + mutex: *mut pthread_mutex_t, + timeout: *const __ygg_timespec_t, +) -> CIntZeroResult { + let cond = cond.ensure_mut(); + let mutex = mutex.ensure_mut(); + let timeout = timeout.ensure(); + cond.wait(mutex, Some(timeout))?; + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_cond_signal(cond: *mut pthread_cond_t) -> CIntZeroResult { + let cond = cond.ensure_mut(); + cond.signal(1); + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> CIntZeroResult { + let cond = cond.ensure_mut(); + cond.signal(u32::MAX); + CIntZeroResult::SUCCESS +} + +// Attributes + +#[no_mangle] +unsafe extern "C" fn pthread_condattr_init(_attr: *mut pthread_condattr_t) -> CIntZeroResult { + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_condattr_destroy(_attr: *mut pthread_condattr_t) -> CIntZeroResult { + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_condattr_getclock( + _attr: *const pthread_condattr_t, + clockid: *mut clockid_t, +) -> CIntZeroResult { + if let Some(clockid) = NonNull::new(clockid) { + clockid.write(CLOCK_MONOTONIC); + } + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_condattr_getpshared( + _attr: *const pthread_condattr_t, + shared: *mut c_int, +) -> CIntZeroResult { + if let Some(shared) = NonNull::new(shared) { + shared.write(PTHREAD_PROCESS_PRIVATE); + } + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_condattr_setclock( + _attr: *mut pthread_condattr_t, + clockid: clockid_t, +) -> CIntZeroResult { + if clockid == CLOCK_MONOTONIC { + CIntZeroResult::SUCCESS + } else { + yggdrasil_rt::debug_trace!("TODO: pthread_condattr_setclock()"); + error::errno = errno::EINVAL; + CIntZeroResult::ERROR + } +} + +#[no_mangle] +unsafe extern "C" fn pthread_condattr_setpshared( + _attr: *mut pthread_condattr_t, + shared: c_int, +) -> CIntZeroResult { + if shared == PTHREAD_PROCESS_PRIVATE { + CIntZeroResult::SUCCESS + } else { + yggdrasil_rt::debug_trace!("TODO: pthread_condattr_setpshared()"); + error::errno = errno::EINVAL; + CIntZeroResult::ERROR + } +} diff --git a/userspace/lib/ygglibc/src/headers/pthread/mod.rs b/userspace/lib/ygglibc/src/headers/pthread/mod.rs index eeadadaa..7c3953fe 100644 --- a/userspace/lib/ygglibc/src/headers/pthread/mod.rs +++ b/userspace/lib/ygglibc/src/headers/pthread/mod.rs @@ -4,6 +4,7 @@ pub mod attr; pub mod barrier; pub mod cond; pub mod mutex; +pub mod once; pub mod rwlock; pub mod spin; pub mod thread; @@ -14,7 +15,6 @@ pub mod tls; // PTHREAD_CANCEL_DEFERRED // PTHREAD_CANCEL_DISABLE // PTHREAD_CANCELED -// PTHREAD_ONCE_INIT // // Thread @@ -43,5 +43,3 @@ pub const PTHREAD_MUTEX_DEFAULT: c_int = PTHREAD_MUTEX_NORMAL; pub const PTHREAD_MUTEX_STALLED: c_int = 0; pub const PTHREAD_MUTEX_ROBUST: c_int = 1; - -// int pthread_once(pthread_once_t *, void (*)(void)); diff --git a/userspace/lib/ygglibc/src/headers/pthread/mutex.rs b/userspace/lib/ygglibc/src/headers/pthread/mutex.rs index cc533bdb..e54d186e 100644 --- a/userspace/lib/ygglibc/src/headers/pthread/mutex.rs +++ b/userspace/lib/ygglibc/src/headers/pthread/mutex.rs @@ -38,7 +38,7 @@ impl pthread_mutex_t { } } - fn lock(&self) { + pub fn lock(&self) { loop { if self.try_lock() { return; @@ -54,9 +54,9 @@ impl pthread_mutex_t { }; } - unsafe fn release(&self) { + pub unsafe fn release(&self) { if self.__state.swap(Self::UNLOCKED, Ordering::Release) == Self::LOCKED { - yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::Wake).ok(); + yggdrasil_rt::sys::mutex(&self.__state, &MutexOperation::Wake(1)).ok(); // TODO maybe yield for a fairer mutex? } } diff --git a/userspace/lib/ygglibc/src/headers/pthread/once.rs b/userspace/lib/ygglibc/src/headers/pthread/once.rs new file mode 100644 index 00000000..066af7e3 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/pthread/once.rs @@ -0,0 +1,27 @@ +// As macro: PTHREAD_ONCE_INIT + +use core::sync::atomic::Ordering; + +use crate::{error::CIntZeroResult, headers::sys_types::pthread_once_t, util::PointerExt}; + +impl pthread_once_t { + fn once(&self, constructor: extern "C" fn()) { + if self.__state.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { + return; + } + constructor(); + } +} + +#[no_mangle] +unsafe extern "C" fn pthread_once( + once: *mut pthread_once_t, + constructor: extern "C" fn(), +) -> CIntZeroResult { + let once = once.ensure_mut(); + assert_ne!(constructor as usize, 0); + + once.once(constructor); + + CIntZeroResult::SUCCESS +} diff --git a/userspace/lib/ygglibc/src/headers/pthread/tls.rs b/userspace/lib/ygglibc/src/headers/pthread/tls.rs index 9743a870..3cc3d1c1 100644 --- a/userspace/lib/ygglibc/src/headers/pthread/tls.rs +++ b/userspace/lib/ygglibc/src/headers/pthread/tls.rs @@ -1,5 +1,50 @@ +use core::{ffi::c_void, ptr::null_mut}; -// void *pthread_getspecific(pthread_key_t); -// int pthread_key_create(pthread_key_t *, void (*)(void*)); -// int pthread_key_delete(pthread_key_t); -// int pthread_setspecific(pthread_key_t, const void *); +use yggdrasil_rt::process::thread_local; + +use crate::{ + error::{self, CIntZeroResult, ResultExt}, + headers::{errno, sys_types::pthread_key_t}, + util::PointerExt, +}; + +#[no_mangle] +unsafe extern "C" fn pthread_key_create( + key: *mut pthread_key_t, + _destructor: extern "C" fn(*mut c_void), +) -> CIntZeroResult { + let key = key.ensure_mut(); + let dtv = thread_local::get_dtv(); + *key = dtv.new_specific(); + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_key_delete(_key: pthread_key_t) -> CIntZeroResult { + yggdrasil_rt::debug_trace!("TODO: pthread_key_delete()"); + CIntZeroResult::SUCCESS +} + +#[no_mangle] +unsafe extern "C" fn pthread_getspecific(key: pthread_key_t) -> *mut c_void { + let dtv = thread_local::get_dtv(); + if key == 0 { + error::errno = errno::EINVAL; + return null_mut(); + } + match dtv.get_specific(key) { + Some(value) => value, + None => { + error::errno = errno::EINVAL; + null_mut() + } + } +} + +#[no_mangle] +unsafe extern "C" fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> CIntZeroResult { + let dtv = thread_local::get_dtv(); + dtv.try_set_specific(key, value, false) + .e_map_err(|_| errno::EINVAL)?; + CIntZeroResult::SUCCESS +} diff --git a/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml b/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml index 8036b501..e140b690 100644 --- a/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml +++ b/userspace/lib/ygglibc/src/headers/sys_types/cbindgen.toml @@ -1,7 +1,12 @@ language = "C" style = "Type" -sys_includes = ["stdint.h", "stddef.h", "bits/sys_types.h"] +sys_includes = [ + "stdbool.h", + "stdint.h", + "stddef.h", + "bits/sys_types.h" +] no_includes = true include_guard = "_SYS_TYPES_H" diff --git a/userspace/lib/ygglibc/src/headers/sys_types/mod.rs b/userspace/lib/ygglibc/src/headers/sys_types/mod.rs index b27fa74c..7a8ee324 100644 --- a/userspace/lib/ygglibc/src/headers/sys_types/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_types/mod.rs @@ -1,4 +1,4 @@ -use core::{ffi::{c_int, c_ulong, c_void}, ptr::NonNull, sync::atomic::AtomicU32}; +use core::{ffi::{c_int, c_ulong, c_void}, ptr::NonNull, sync::atomic::{AtomicBool, AtomicU32}}; #[cfg(any(target_pointer_width = "64", rust_analyzer))] mod bits64; @@ -61,11 +61,13 @@ pub struct pthread_barrierattr_t {} #[repr(C)] pub struct pthread_cond_t { - __dummy: c_int + pub(crate) __read: AtomicU32, + pub(crate) __write: AtomicU32, } +#[repr(C)] pub struct pthread_condattr_t {} -pub struct pthread_key_t {} +pub type pthread_key_t = usize; #[repr(C)] pub struct pthread_mutex_t { @@ -74,7 +76,10 @@ pub struct pthread_mutex_t { #[repr(C)] pub struct pthread_mutexattr_t {} -pub struct pthread_once_t {} +#[repr(C)] +pub struct pthread_once_t { + pub(crate) __state: AtomicBool +} #[repr(C)] pub struct pthread_rwlock_t { diff --git a/userspace/lib/ygglibc/src/headers/time/cbindgen.toml b/userspace/lib/ygglibc/src/headers/time/cbindgen.toml index 305f66d3..b6800848 100644 --- a/userspace/lib/ygglibc/src/headers/time/cbindgen.toml +++ b/userspace/lib/ygglibc/src/headers/time/cbindgen.toml @@ -2,10 +2,10 @@ language = "C" style = "Tag" sys_includes = [ + "sys/types.h", "stddef.h", "stdint.h", "locale.h", - "sys/types.h", "sys/time.h", "bits/time.h", ] diff --git a/userspace/lib/ygglibc/src/init.rs b/userspace/lib/ygglibc/src/init.rs index 10edd103..41840840 100644 --- a/userspace/lib/ygglibc/src/init.rs +++ b/userspace/lib/ygglibc/src/init.rs @@ -7,7 +7,7 @@ static INIT_COMPLETE: AtomicBool = AtomicBool::new(false); // TODO implement topological sorting for proper .init_array calls in dyn-loader #[link_section = ".preinit_array"] #[used] -static PREINIT_ARRAY: [extern "C" fn (); 1] = [init]; +static PREINIT_ARRAY: [extern "C" fn(); 1] = [init]; pub extern "C" fn init() { if INIT_COMPLETE.swap(true, Ordering::Acquire) { @@ -19,3 +19,44 @@ pub extern "C" fn init() { io::init(); } } + +unsafe fn call_constructor_list(start: usize, end: usize) { + yggdrasil_rt::debug_trace!("call_constructor_list {:#x?}", start..end); + let base = start as *const usize; + let len = (end - start) / size_of::(); + for i in 0..len { + let addr = base.add(i).read(); + if addr == 0 { + continue; + } + yggdrasil_rt::debug_trace!("call constructor: {:#x}", addr); + let func = core::mem::transmute::<_, extern "C" fn()>(addr); + func(); + } +} + +pub fn call_constructors() { + extern "C" { + static __preinit_array_start: u8; + static __preinit_array_end: u8; + + static __init_array_start: u8; + static __init_array_end: u8; + } + + // Global constructors already called + if INIT_COMPLETE.load(Ordering::Acquire) { + return; + } + + unsafe { + call_constructor_list( + (&raw const __preinit_array_start).addr(), + (&raw const __preinit_array_end).addr(), + ); + call_constructor_list( + (&raw const __init_array_start).addr(), + (&raw const __init_array_end).addr(), + ); + } +} diff --git a/userspace/lib/ygglibc/src/lib.rs b/userspace/lib/ygglibc/src/lib.rs index 60a2ec6a..b05092b4 100644 --- a/userspace/lib/ygglibc/src/lib.rs +++ b/userspace/lib/ygglibc/src/lib.rs @@ -62,6 +62,7 @@ unsafe extern "C" fn __ygglibc_entry( main: extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int, arg: usize, ) { + init::call_constructors(); signal::init(true); init::init(); diff --git a/userspace/lib/ygglibc/src/panic.rs b/userspace/lib/ygglibc/src/panic.rs index fd91c45e..eb9f887a 100644 --- a/userspace/lib/ygglibc/src/panic.rs +++ b/userspace/lib/ygglibc/src/panic.rs @@ -74,10 +74,15 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! { 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 Some(location) = pi.location() { + writeln!(printer, "{}:{}:", location.file(), location.line()).ok(); + } + writeln!(printer, " {}", pi.message()).ok(); + + let pthread_self = Thread::this(); if let EResult::Ok(this) = pthread_self { if this == 0 { writeln!(printer, "* Main thread panicked *").ok(); @@ -85,14 +90,11 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! { writeln!(printer, "* Thread {} panicked*", this).ok(); } } - if let Some(location) = pi.location() { - writeln!(printer, "{}:{}:", location.file(), location.line()).ok(); - } - writeln!(printer, " {}", pi.message()).ok(); printer.flush(); } 1 => { + yggdrasil_rt::debug_trace!("{pi:?}"); yggdrasil_rt::debug_trace!("!!! ygglibc panicked while panicking !!!"); } _ => {} diff --git a/xtask/src/build/c.rs b/xtask/src/build/c.rs index c7bdbc5d..7446992b 100644 --- a/xtask/src/build/c.rs +++ b/xtask/src/build/c.rs @@ -57,6 +57,22 @@ fn build_test_cpp_program( install.push((target_dir.join("cpp-test"), "cpp-test".into())); + log::info!("Building a test C++ program [static]"); + let target_dir = &env.userspace_output_dir; + + let mut command = llvm.c_clangpp(env); + command + .args(["-static", "-O0", "-ggdb", "-fstack-protector-strong"]) + .arg("-o") + .arg(target_dir.join("cpp-test-static")) + .arg(env.workspace_root.join("test.cpp")); + + if !command.status()?.success() { + return Err(Error::ExternalCommandFailed); + } + + install.push((target_dir.join("cpp-test-static"), "cpp-test-static".into())); + Ok(()) } diff --git a/xtask/src/build/toolchain_c.rs b/xtask/src/build/toolchain_c.rs index 3e3fdcf1..23f2169e 100644 --- a/xtask/src/build/toolchain_c.rs +++ b/xtask/src/build/toolchain_c.rs @@ -125,12 +125,12 @@ pub fn install_llvm_cxx_runtime(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error command .arg(format!("--toolchain={}", toolchain_cmake.display())) .arg("-GNinja") - .arg("-DCMAKE_BUILD_TYPE=RelWithDebInfo") + .arg("-DCMAKE_BUILD_TYPE=Debug") .arg("-DLLVM_ENABLE_RUNTIMES=libcxxabi;libcxx") .arg("-DLIBCXXABI_ENABLE_STATIC=ON") .arg("-DLIBCXXABI_ENABLE_SHARED=OFF") .arg("-DLIBCXXABI_USE_COMPILER_RT=ON") - .arg("-DLIBCXXABI_ENABLE_THREADS=OFF") + .arg("-DLIBCXXABI_ENABLE_THREADS=ON") .arg("-DLIBCXXABI_ENABLE_EXCEPTIONS=OFF") .arg("-DLIBCXXABI_USE_LLVM_UNWINDER=OFF") .arg("-DLIBCXX_CXX_ABI=libcxxabi") @@ -138,7 +138,7 @@ pub fn install_llvm_cxx_runtime(env: &BuildEnv, llvm: &Llvm) -> Result<(), Error .arg("-DLIBCXX_STATICALLY_LINK_ABI_IN_SHARED_LIBRARY=ON") .arg("-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=ON") .arg("-DLIBCXX_ENABLE_FILESYSTEM=ON") - .arg("-DLIBCXX_ENABLE_THREADS=OFF") + .arg("-DLIBCXX_ENABLE_THREADS=ON") .arg("-DLIBCXX_ENABLE_EXCEPTIONS=OFF") .arg("-DLIBCXX_ENABLE_SHARED=ON") .arg("-DLIBCXX_ENABLE_STATIC=ON")