libc: properly set up main thread
This commit is contained in:
parent
dea1b3ecf2
commit
03f6362756
1
test.c
1
test.c
@ -1,6 +1,7 @@
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/yggdrasil.h>
|
||||
|
||||
_Thread_local int x = 123;
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"
|
14
userspace/lib/ygglibc/src/headers/sys_yggdrasil/mod.rs
Normal file
14
userspace/lib/ygglibc/src/headers/sys_yggdrasil/mod.rs
Normal 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}")
|
||||
}
|
@ -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)]
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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"));
|
||||
|
@ -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 })
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![deny(warnings)]
|
||||
#![feature(exit_status_error)]
|
||||
|
||||
use std::{fs, path::PathBuf, process::ExitCode};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user