libc: properly set up main thread

This commit is contained in:
Mark Poliakov 2024-11-19 00:09:41 +02:00
parent dea1b3ecf2
commit 03f6362756
13 changed files with 156 additions and 53 deletions

1
test.c
View File

@ -1,6 +1,7 @@
#include <pthread.h>
#include <assert.h>
#include <stdio.h>
#include <sys/yggdrasil.h>
_Thread_local int x = 123;

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {
"<null>"
} else if let Ok(str) = CStr::from_ptr(message).to_str() {
str
} else {
"<invalid str>"
};
panic!("Explicit panic: {message}")
}

View File

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

View File

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

View File

@ -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<BTreeMap<pthread_t, Arc<Thread>>> = Mutex::new(BTreeMap::new());
#[thread_local]
static mut SELF: Option<Arc<Thread>> = None;
static SELF: OnceCell<Arc<Thread>> = 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<Arc<Thread>> {
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<Self>) {
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);
}
}

View File

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

View File

@ -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<Llvm, Error> {
.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 })
}

View File

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

View File

@ -1,4 +1,5 @@
#![deny(warnings)]
#![feature(exit_status_error)]
use std::{fs, path::PathBuf, process::ExitCode};