libc: implement threads for libcxx

This commit is contained in:
Mark Poliakov 2024-11-26 22:59:23 +02:00
parent 0742edc516
commit 588e9e2936
30 changed files with 427 additions and 95 deletions

View File

@ -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);
}
}

View File

@ -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"),
}
}
}
}

View File

@ -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(())
}
}

View File

@ -60,10 +60,8 @@ pub enum MutexOperation {
WaitWhileSet(u32, Option<Duration>),
/// Waits on the mutex object until `mutex & A != 0`
WaitUntilSet(u32, Option<Duration>),
/// 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

View File

@ -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");
}
}
}

View File

@ -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() };
}
}

View File

@ -107,7 +107,7 @@ impl<T> Once<T> {
}
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) {

View File

@ -142,7 +142,7 @@ impl<T> RwLock<T> {
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();
}
}

31
test.c
View File

@ -3,24 +3,33 @@
#include <assert.h>
#include <stdio.h>
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;
}

View File

@ -1,21 +1,21 @@
#include <iostream>
#include <cerrno>
#include <fcntl.h>
#include <thread>
int main() {
std::vector<int> 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;
}

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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;

View File

@ -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 {

View File

@ -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

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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));

View File

@ -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?
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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"

View File

@ -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 {

View File

@ -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",
]

View File

@ -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::<usize>();
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(),
);
}
}

View File

@ -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();

View File

@ -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 !!!");
}
_ => {}

View File

@ -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(())
}

View File

@ -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")