libc: proper linkage for setjmp/longjmp

This commit is contained in:
Mark Poliakov 2024-11-13 21:36:50 +02:00
parent 8160f2ab1a
commit 088659ce6c
18 changed files with 432 additions and 111 deletions

19
test.c
View File

@ -1,11 +1,22 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
#include <setjmp.h>
void will_fail(jmp_buf a) {
printf("FAIL!!!\n");
longjmp(a, 123);
}
int main(int argc, const char **argv) { int main(int argc, const char **argv) {
double x = round(1234.123); jmp_buf a;
assert(fabs(x - 1234.000) < 0.00001); int res = setjmp(a);
double y = round(1234.5678);
assert(fabs(y - 1235.000) < 0.00001); printf("setjmp = %d\n", res);
if (res == 0) {
will_fail(a);
}
return 0; return 0;
} }

View File

@ -78,6 +78,7 @@ impl RelocationExt for Rela {
// Direct 64 bit // Direct 64 bit
elf::abi::R_X86_64_64 => Ok(Some(RelValue::QWord(base_value + self.r_addend))), elf::abi::R_X86_64_64 => Ok(Some(RelValue::QWord(base_value + self.r_addend))),
elf::abi::R_X86_64_JUMP_SLOT => Ok(Some(RelValue::QWord(base_value))), elf::abi::R_X86_64_JUMP_SLOT => Ok(Some(RelValue::QWord(base_value))),
elf::abi::R_X86_64_COPY => todo!("{:?}: R_X86_64_COPY", symbol.name),
// GLOB_DAT // GLOB_DAT
elf::abi::R_X86_64_GLOB_DAT => Ok(Some(RelValue::QWord(base_value))), elf::abi::R_X86_64_GLOB_DAT => Ok(Some(RelValue::QWord(base_value))),
// Adjust by image base // Adjust by image base

View File

@ -22,12 +22,7 @@
"linker": "rust-lld", "linker": "rust-lld",
"linker-flavor": "ld.lld", "linker-flavor": "ld.lld",
"late-link-args-static": { "pre-link-args": {
"ld.lld": [
"--gc-sections"
]
},
"late-link-args-dynamic": {
"ld.lld": [ "ld.lld": [
"--dynamic-linker=/libexec/dyn-loader" "--dynamic-linker=/libexec/dyn-loader"
] ]

View File

@ -1,7 +0,0 @@
#ifndef _YGGDRASIL_SETJMP_H
#define _YGGDRASIL_SETJMP_H 1
int setjmp(jmp_buf env);
[[noreturn]] void longjmp(jmp_buf env, int val);
#endif

View File

@ -0,0 +1,14 @@
#ifndef _DLFCN_H
#define _DLFCN_H 1
#define RTLD_LAZY (1 << 0)
#define RTLD_NOW (0 << 0)
#define RTLD_GLOBAL (1 << 1)
#define RTLD_LOCAL (0 << 1)
int dlclose(void *ptr);
char *dlerror(void);
void *dlopen(const char *path, int mode);
void *dlsym(void *handle, const char *sym);
#endif

View File

@ -18,9 +18,7 @@
// Math // Math
// <complex.h> - // <complex.h> -
// <fenv.h> - // <fenv.h> -
// <float.h> - // <math.h> +
// <math.h> -
// <tgmath.h> -
// I/O utilities // I/O utilities
// <cpio.h> ! // <cpio.h> !

View File

@ -1,7 +1,7 @@
language = "C" language = "C"
style = "Type" style = "Type"
sys_includes = ["stdarg.h", "stddef.h"] sys_includes = ["stddef.h"]
no_includes = true no_includes = true
include_guard = "_SETJMP_H" include_guard = "_SETJMP_H"
@ -12,4 +12,11 @@ isize_type = "ssize_t"
[export] [export]
include = ["jmp_buf"] include = ["jmp_buf"]
exclude = [] exclude = [
"setjmp",
"longjmp",
"_setjmp",
"_longjmp",
"__x86_64_setjmp",
"__x86_64_longjmp"
]

View File

@ -1,5 +1,9 @@
#[cfg(any(target_arch = "x86_64", rust_analyzer))] use core::ffi::c_int;
mod x86_64;
#[cfg(any(target_arch = "x86_64", rust_analyzer))] #[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub type jmp_buf = *mut x86_64::__jmp_buf; pub mod x86_64;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub use x86_64::__jmp_buf;
pub type jmp_buf = __jmp_buf;

View File

@ -1,46 +0,0 @@
.global setjmp
.global longjmp
.global _setjmp
.global _longjmp
.section .text
// args:
// %rdi -- __jmp_buf *env
setjmp:
_setjmp:
movq %rbx, 0(%rdi)
movq %rbp, 8(%rdi)
movq %r12, 16(%rdi)
movq %r13, 24(%rdi)
movq %r14, 32(%rdi)
movq %r15, 40(%rdi)
// Calculate return stack and rip
leaq 8(%rsp), %rax
movq %rax, 48(%rdi)
movq (%rsp), %rax
movq %rax, 56(%rdi)
movq $0, %rax
ret
// args:
// %rdi -- __jmp_buf *env
// %rsi -- int val
longjmp:
_longjmp:
// Restore registers
movq 0(%rdi), %rbx
movq 8(%rdi), %rbp
movq 16(%rdi), %r12
movq 24(%rdi), %r13
movq 32(%rdi), %r14
movq 40(%rdi), %r15
movq 48(%rdi), %rsp
movq 56(%rdi), %rax
pushq %rax
// Setup return value
movq %rsi, %rax
ret

View File

@ -1,15 +1,53 @@
use core::arch::global_asm; use core::{arch::global_asm, ffi::c_int};
#[repr(C)] pub type __jmp_buf = [usize; 8];
pub struct __jmp_buf {
rip: usize, #[no_mangle]
rsp: usize, #[naked]
r15: usize, unsafe extern "C" fn setjmp(buf: __jmp_buf) -> c_int {
r14: usize, // %rdi -- jmp_buf pointer
r13: usize, core::arch::naked_asm!(
r12: usize, r#"
rbp: usize, // Preserve callee-saved registers
rbx: usize, movq %rbx, 0(%rdi) // jmp_buf[0] = %rbx
movq %rbp, 8(%rdi) // jmp_buf[1] = %rbp
movq %r12, 16(%rdi) // jmp_buf[2] = %r12
movq %r13, 24(%rdi) // jmp_buf[3] = %r13
movq %r14, 32(%rdi) // jmp_buf[4] = %r14
movq %r15, 40(%rdi) // jmp_buf[5] = %r15
// Calculate return stack and rip
leaq 8(%rsp), %rax
movq %rax, 48(%rdi) // jmp_buf[6] = %rsp + 8 (return stack)
movq (%rsp), %rax // jmp_buf[7] = return address
movq %rax, 56(%rdi)
movq $0, %rax
ret
"#,
options(att_syntax)
)
} }
global_asm!(include_str!("x86_64.S"), options(att_syntax)); #[no_mangle]
#[naked]
unsafe extern "C" fn longjmp(buf: __jmp_buf, val: c_int) -> c_int {
// %rdi -- jmp_buf pointer
// %rsi -- return value
core::arch::naked_asm!(r#"
// Restore the registers
movq 0(%rdi), %rbx
movq 8(%rdi), %rbp
movq 16(%rdi), %r12
movq 24(%rdi), %r13
movq 32(%rdi), %r14
movq 40(%rdi), %r15
movq 48(%rdi), %rsp
movq 56(%rdi), %rax
pushq %rax // Setup fake return address
// Setup return value
movq %rsi, %rax
ret
"#, options(att_syntax))
}

View File

@ -1,9 +1,10 @@
language = "C" language = "C"
style = "Type" style = "Tag"
sys_includes = [ sys_includes = [
"stddef.h", "stddef.h",
"stdint.h", "stdint.h",
"time.h",
"sys/types.h", "sys/types.h",
"bits/signal.h" "bits/signal.h"
] ]
@ -15,5 +16,5 @@ usize_type = "size_t"
isize_type = "ssize_t" isize_type = "ssize_t"
[export] [export]
include = ["sig_handler_t", "SIGABRT"] include = ["sig_handler_t", "sigaction", "sigevent", "SIGABRT"]
exclude = [] exclude = []

View File

@ -1,11 +1,59 @@
use core::ffi::c_int; use core::ffi::{c_char, c_int, c_long, c_void};
use yggdrasil_rt::process::Signal; use yggdrasil_rt::process::Signal;
use super::sys_types::pid_t; use super::{
sys_time::__ygg_timespec_t,
sys_types::{pid_t, uid_t},
};
pub type sig_handler_t = unsafe extern "C" fn(SigNumber); pub type sig_handler_t = unsafe extern "C" fn(SigNumber);
pub type sigset_t = u64;
#[repr(C)]
pub struct sigevent {
pub sigev_notify: c_int,
pub sigev_signo: c_int,
pub sigev_value: sigval,
pub sigev_notify_function: unsafe extern "C" fn(sigval),
// TODO pthread_attr_t *
pub sigev_notify_attributes: *mut c_void,
}
#[repr(C)]
pub struct sigaction {
pub sa_handler: unsafe extern "C" fn(c_int),
pub sa_mask: sigset_t,
pub sa_flags: c_int,
pub sa_sigaction: unsafe extern "C" fn(c_int, *mut siginfo_t, *mut c_void),
}
#[repr(C)]
pub struct siginfo {
pub si_signo: c_int,
pub si_code: c_int,
pub si_errno: c_int,
pub si_pid: pid_t,
pub si_uid: uid_t,
pub si_addr: *mut c_void,
pub si_status: c_int,
pub si_band: c_long,
pub si_value: sigval,
}
pub type siginfo_t = siginfo;
#[repr(C)]
pub union sigval {
pub sival_int: c_int,
pub sival_ptr: *mut c_void,
}
// TODO sig_atomic_t
// SIG_DFL, SIG_ERR, SIG_HOLD, SIG_IGN are in <bits/signal.h>
extern "C" { extern "C" {
fn __sig_terminate(_: SigNumber); fn __sig_terminate(_: SigNumber);
fn __sig_ignore(_: SigNumber); fn __sig_ignore(_: SigNumber);
@ -15,6 +63,10 @@ extern "C" {
#[repr(transparent)] #[repr(transparent)]
pub struct SigNumber(pub c_int); pub struct SigNumber(pub c_int);
pub const SIGEV_NONE: c_int = 0;
pub const SIGEV_SIGNAL: c_int = 1;
pub const SIGEV_THREAD: c_int = 2;
// TODO generate these based on the ABI // TODO generate these based on the ABI
pub const SIGSEGV: SigNumber = SigNumber(1); pub const SIGSEGV: SigNumber = SigNumber(1);
pub const SIGABRT: SigNumber = SigNumber(2); pub const SIGABRT: SigNumber = SigNumber(2);
@ -77,30 +129,159 @@ impl From<Signal> for SigNumber {
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn kill(_pid: pid_t, _signum: SigNumber) -> c_int { unsafe extern "C" fn kill(_pid: pid_t, _signum: c_int) -> c_int {
todo!() todo!()
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn killpg(_pid: pid_t, _signum: SigNumber) -> c_int { unsafe extern "C" fn killpg(_pgid: pid_t, _signum: c_int) -> c_int {
todo!() todo!()
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn raise(_signum: SigNumber) -> c_int { unsafe extern "C" fn psiginfo(_info: *const siginfo_t, _m: *const c_char) {
todo!() todo!()
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn signal(_signum: SigNumber, _handler: sig_handler_t) -> sig_handler_t { unsafe extern "C" fn psignal(_signum: c_int, _m: *const c_char) {
todo!()
}
// TODO pthread_t
#[no_mangle]
unsafe extern "C" fn pthread_kill(_pt: *mut c_void, _signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn pthread_sigmask(
_a: c_int,
_new: *const sigset_t,
_old: *mut sigset_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn raise(_signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigaction(
_signum: c_int,
_new: *const sigaction,
_old: *mut sigaction,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigaddset(_mask: *mut sigset_t, _signum: c_int) -> c_int {
todo!()
}
// TODO: stack_t
#[no_mangle]
unsafe extern "C" fn sigaltstack(_new: *const c_void, _old: *const c_void) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigdelset(_mask: *mut sigset_t, _signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigemptyset(_mask: *mut sigset_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigfillset(_mask: *mut sigset_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sighold(_signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigignore(_signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn siginterrupt(_signum: c_int, _b: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigismember(_mask: *const sigset_t, _signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn signal(_handler: sig_handler_t, _signum: c_int) -> sig_handler_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigpause(_signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigpending(_mask: *mut sigset_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigprocmask(
_signum: c_int,
_new: *const sigset_t,
_old: *mut sigset_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigqueue(_pid: pid_t, _signum: c_int, _val: sigval) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigrelse(_signum: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigset(_signum: c_int, _handler: sig_handler_t) -> sig_handler_t {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigsuspend(_mask: *const sigset_t) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigtimedwait(
_mask: *const sigset_t,
_info: *mut siginfo_t,
_time: *const __ygg_timespec_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigwait(_mask: *const sigset_t, _signum: *mut c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn sigwaitinfo(_mask: *const sigset_t, _info: *mut siginfo_t) -> c_int {
todo!() todo!()
// // NOTE handler might be NULL, so check that
// let Ok(signal) = Signal::try_from(signum) else {
// // Ignore
// return __sig_terminate;
// };
// let handler_ptr = handler as usize;
// let handler = (handler_ptr != 0).then_some(handler);
// signal::set_handler(signal, handler)
} }

View File

@ -1,8 +1,36 @@
use core::ffi::{c_char, c_int};
// int grantpt(int); #[no_mangle]
// char *mkdtemp(char *); unsafe extern "C" fn grantpt(_fd: c_int) -> c_int {
// int mkstemp(char *); todo!()
// int posix_openpt(int); }
// char *ptsname(int);
// char *realpath(const char *restrict, char *restrict); #[no_mangle]
// int unlockpt(int); unsafe extern "C" fn mkdtemp(_template: *mut c_char) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn mkstemp(_template: *mut c_char) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn posix_openpt(_fd: c_int) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn ptsname(_fd: c_int) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn realpath(_path: *const c_char, _resolved: *mut c_char) -> *mut c_char {
todo!()
}
#[no_mangle]
unsafe extern "C" fn unlockpt(_fd: c_int) -> c_int {
todo!()
}

View File

@ -6,7 +6,7 @@ use core::{
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc}; use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Utc};
use super::{sys_time::__ygg_timespec_t, sys_types::time_t}; use super::{sys_time::__ygg_timespec_t, sys_types::{clock_t, clockid_t, time_t}};
mod convert; mod convert;
mod string; mod string;
@ -36,6 +36,15 @@ pub struct tm {
pub type __ygg_tm_t = tm; pub type __ygg_tm_t = tm;
pub const CLOCKS_PER_SEC: clock_t = 1000000;
pub const CLOCK_MONOTONIC: clockid_t = 1;
pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2;
pub const CLOCK_REALTIME: clockid_t = 3;
pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 4;
pub const TIMER_ABSTIME: c_int = 1 << 0;
#[no_mangle] #[no_mangle]
pub static mut daylight: c_int = 0; pub static mut daylight: c_int = 0;
#[no_mangle] #[no_mangle]

View File

@ -25,7 +25,7 @@ unsafe extern "C" fn nanosleep(
#[no_mangle] #[no_mangle]
unsafe extern "C" fn time(_tp: *mut time_t) -> time_t { unsafe extern "C" fn time(_tp: *mut time_t) -> time_t {
todo!() time_t(0)
} }
#[no_mangle] #[no_mangle]

View File

@ -8,7 +8,8 @@
maybe_uninit_slice, maybe_uninit_slice,
slice_internals, slice_internals,
linkage, linkage,
rustc_private rustc_private,
naked_functions
)] )]
#![allow(internal_features)] #![allow(internal_features)]
#![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_std)]

View File

@ -1,7 +1,88 @@
use core::{fmt, sync::atomic::AtomicUsize};
use crate::io::{managed::{stderr, FILE}, Write};
struct PanicPrinter {
buffer: [u8; 1024],
pos: usize,
print: Option<&'static mut FILE>
}
impl fmt::Write for PanicPrinter {
fn write_str(&mut self, s: &str) -> fmt::Result {
for ch in s.bytes() {
self.push(ch);
}
Ok(())
}
}
impl PanicPrinter {
pub fn new() -> Self {
let print = unsafe { stderr.as_mut() };
Self {
buffer: [0; 1024],
pos: 0,
print,
}
}
fn flush(&mut self) {
let text = &self.buffer[..self.pos];
if !text.is_empty() {
// Print to debug trace
unsafe { yggdrasil_rt::debug::trace_raw(text) };
// Print to stderr
if let Some(print) = self.print.as_mut() {
print.write_all(text).ok();
print.write_all(b"\n").ok();
}
}
self.pos = 0;
}
pub fn push(&mut self, ch: u8) {
if self.pos >= self.buffer.len() {
return;
}
if ch == b'\n' {
self.flush();
} else {
self.buffer[self.pos] = ch;
self.pos += 1;
}
}
}
static PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
#[cfg(any(not(test), rust_analyzer))] #[cfg(any(not(test), rust_analyzer))]
#[panic_handler] #[panic_handler]
fn panic_handler(pi: &core::panic::PanicInfo) -> ! { fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
yggdrasil_rt::debug_trace!("PANIC {:?}", &pi); use core::{fmt::Write, sync::atomic::Ordering};
match PANIC_COUNT.fetch_add(1, Ordering::Relaxed) {
0 => {
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());
printer.flush();
}
1 => unsafe {
yggdrasil_rt::debug_trace!("!!! ygglibc panicked while panicking !!!");
}
_ => {}
}
// Kill myself // Kill myself
super::process::abort(); super::process::abort();

View File

@ -196,6 +196,11 @@ pub fn build_initrd(
build_userspace(env, &mut install_extra, check)?; build_userspace(env, &mut install_extra, check)?;
install_extra.push((
"toolchain-c/programs/lua-5.4.7/src/lua".into(),
"lua".into(),
));
build_rootfs( build_rootfs(
env, env,
install_extra, install_extra,