diff --git a/Cargo.lock b/Cargo.lock index 2dbd6be7ed..1710b7ff4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.99" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" +checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" [[package]] name = "cfg-if" @@ -143,6 +143,10 @@ dependencies = [ "version_check", ] +[[package]] +name = "generic-rt" +version = "0.1.0" + [[package]] name = "goblin" version = "0.7.1" @@ -194,9 +198,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "md-5" @@ -214,15 +218,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -272,9 +267,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.85" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -360,21 +355,23 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "redox-exec" -version = "0.1.0" -dependencies = [ - "goblin", - "plain", - "redox_syscall", -] - [[package]] name = "redox-path" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64072665120942deff5fd5425d6c1811b854f4939e7f1c01ce755f64432bbea7" +[[package]] +name = "redox-rt" +version = "0.1.0" +dependencies = [ + "bitflags", + "generic-rt", + "goblin", + "plain", + "redox_syscall", +] + [[package]] name = "redox_event" version = "0.4.0" @@ -387,9 +384,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags", ] @@ -404,19 +401,19 @@ dependencies = [ "cbitset", "cc", "dlmalloc", + "generic-rt", "goblin", "libc", "md-5", "memchr", - "memoffset", "pbkdf2", "plain", "posix-regex", "rand", "rand_jitter", "rand_xorshift", - "redox-exec", "redox-path", + "redox-rt", "redox_event", "redox_syscall", "sc", @@ -496,15 +493,15 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.66" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 49e6a5bd58..e3ebeb48ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,9 @@ members = [ "src/crt0", "src/crti", "src/crtn", - "src/platform/redox/redox-exec", + "redox-rt", "ld_so", + "generic-rt", ] exclude = ["tests", "dlmalloc-rs"] @@ -24,7 +25,6 @@ cc = "1" [dependencies] bitflags = "2" cbitset = "0.2" -memoffset = "0.9" posix-regex = { path = "posix-regex", features = ["no_std"] } # TODO: For some reason, rand_jitter hasn't been updated to use the latest rand_core @@ -43,6 +43,7 @@ bcrypt-pbkdf = { version = "0.10", default-features = false, features = ["alloc" scrypt = { version = "0.11", default-features = false, features = ["simple"]} pbkdf2 = { version = "0.12", features = ["sha2"]} sha2 = { version = "0.10", default-features = false } +generic-rt = { path = "generic-rt" } [dependencies.goblin] version = "0.7" @@ -58,8 +59,8 @@ features = ["c_api"] sc = "0.2.3" [target.'cfg(target_os = "redox")'.dependencies] -redox_syscall = "0.5.1" -redox-exec = { path = "src/platform/redox/redox-exec" } +redox_syscall = "0.5.3" +redox-rt = { path = "redox-rt" } redox-path = "0.2" redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git", default-features = false, features = ["redox_syscall"] } diff --git a/generic-rt/Cargo.toml b/generic-rt/Cargo.toml new file mode 100644 index 0000000000..11e01ee046 --- /dev/null +++ b/generic-rt/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "generic-rt" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/generic-rt/src/lib.rs b/generic-rt/src/lib.rs new file mode 100644 index 0000000000..2999115c36 --- /dev/null +++ b/generic-rt/src/lib.rs @@ -0,0 +1,112 @@ +#![no_std] +#![feature(core_intrinsics)] + +use core::arch::asm; +use core::mem::{self, offset_of}; + +#[derive(Debug)] +#[repr(C)] +pub struct GenericTcb { + /// Pointer to the end of static TLS. Must be the first member + pub tls_end: *mut u8, + /// Size of the memory allocated for the static TLS in bytes (multiple of page size) + pub tls_len: usize, + /// Pointer to this structure + pub tcb_ptr: *mut Self, + /// Size of the memory allocated for this structure in bytes (should be same as page size) + pub tcb_len: usize, + pub os_specific: Os, +} +impl GenericTcb { + /// Architecture specific code to read a usize from the TCB - aarch64 + #[inline(always)] + #[cfg(target_arch = "aarch64")] + pub unsafe fn arch_read(offset: usize) -> usize { + let abi_ptr: usize; + asm!( + "mrs {}, tpidr_el0", + out(reg) abi_ptr, + ); + + let tcb_ptr = *(abi_ptr as *const usize); + *((tcb_ptr + offset) as *const usize) + } + + /// Architecture specific code to read a usize from the TCB - x86 + #[inline(always)] + #[cfg(target_arch = "x86")] + pub unsafe fn arch_read(offset: usize) -> usize { + let value; + asm!( + " + mov {}, gs:[{}] + ", + out(reg) value, + in(reg) offset, + ); + value + } + + /// Architecture specific code to read a usize from the TCB - x86_64 + #[inline(always)] + #[cfg(target_arch = "x86_64")] + pub unsafe fn arch_read(offset: usize) -> usize { + let value; + asm!( + " + mov {}, fs:[{}] + ", + out(reg) value, + in(reg) offset, + ); + value + } + + pub unsafe fn current_ptr() -> Option<*mut Self> { + let tcb_ptr = Self::arch_read(offset_of!(Self, tcb_ptr)) as *mut Self; + let tcb_len = Self::arch_read(offset_of!(Self, tcb_len)); + if tcb_ptr.is_null() || tcb_len < mem::size_of::() { + None + } else { + Some(tcb_ptr) + } + } + pub unsafe fn current() -> Option<&'static mut Self> { + Some(&mut *Self::current_ptr()?) + } +} +pub fn panic_notls(msg: impl core::fmt::Display) -> ! { + //eprintln!("panicked in ld.so: {}", msg); + + core::intrinsics::abort(); +} + +pub trait ExpectTlsFree { + type Unwrapped; + + fn expect_notls(self, msg: &str) -> Self::Unwrapped; +} +impl ExpectTlsFree for Result { + type Unwrapped = T; + + fn expect_notls(self, msg: &str) -> T { + match self { + Ok(t) => t, + Err(err) => panic_notls(format_args!( + "{}: expect failed for Result with err: {:?}", + msg, err + )), + } + } +} +impl ExpectTlsFree for Option { + type Unwrapped = T; + + fn expect_notls(self, msg: &str) -> T { + match self { + Some(t) => t, + None => panic_notls(format_args!("{}: expect failed for Option", msg)), + } + } +} + diff --git a/include/bits/signal.h b/include/bits/signal.h index 46cc2553ed..57832b5e36 100644 --- a/include/bits/signal.h +++ b/include/bits/signal.h @@ -5,4 +5,18 @@ #define SIG_IGN ((void (*)(int))1) #define SIG_ERR ((void (*)(int))-1) +struct siginfo; +typedef struct siginfo siginfo_t; +typedef unsigned long long sigset_t; + +struct sigaction { + union { + void (*sa_handler)(int); + void (*sa_sigaction)(int, siginfo_t *, void *); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; +}; + #endif // _BITS_SIGNAL_H diff --git a/src/platform/redox/redox-exec/Cargo.toml b/redox-rt/Cargo.toml similarity index 68% rename from src/platform/redox/redox-exec/Cargo.toml rename to redox-rt/Cargo.toml index 18212a3457..33502bb75f 100644 --- a/src/platform/redox/redox-exec/Cargo.toml +++ b/redox-rt/Cargo.toml @@ -1,13 +1,17 @@ [package] -name = "redox-exec" +name = "redox-rt" authors = ["4lDO2 <4lDO2@protonmail.com>"] version = "0.1.0" edition = "2021" license = "MIT" +description = "Libc-independent runtime for Redox" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -redox_syscall = "0.5.1" +bitflags = "2" goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] } plain = "0.2" +redox_syscall = "0.5.3" + +generic-rt = { path = "../generic-rt" } diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs new file mode 100644 index 0000000000..e4a5e8b54c --- /dev/null +++ b/redox-rt/src/arch/aarch64.rs @@ -0,0 +1,292 @@ +use core::mem::offset_of; + +use syscall::data::*; +use syscall::error::*; + +use crate::proc::{fork_inner, FdGuard}; +use crate::signal::SigStack; +use crate::signal::{inner_c, RtSigarea, PROC_CONTROL_STRUCT}; +use crate::Tcb; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub(crate) const STACK_TOP: usize = 1 << 47; +pub(crate) const STACK_SIZE: usize = 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub altstack_top: usize, + pub altstack_bottom: usize, + pub tmp_x1_x2: [usize; 2], + pub tmp_x3_x4: [usize; 2], + pub tmp_sp: usize, + pub onstack: u64, + pub disable_signals_depth: u64, + pub pctl: usize, // TODO: remove +} +#[repr(C)] +#[derive(Debug, Default)] +pub struct ArchIntRegs { + pub x30: usize, + pub x29: usize, + pub x28: usize, + pub x27: usize, + pub x26: usize, + pub x25: usize, + pub x24: usize, + pub x23: usize, + pub x22: usize, + pub x21: usize, + pub x20: usize, + pub x19: usize, + pub x18: usize, + pub x17: usize, + pub x16: usize, + pub x15: usize, + pub x14: usize, + pub x13: usize, + pub x12: usize, + pub x11: usize, + pub x10: usize, + pub x9: usize, + pub x8: usize, + pub x7: usize, + pub x6: usize, + pub x5: usize, + pub x4: usize, + pub x3: usize, + pub x2: usize, + pub x1: usize, + + pub sp: usize, + pub nzcv: usize, // user-accessible PSTATE bits + + pub pc: usize, + pub x0: usize, +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?); + + env.tpidr_el0 = 0; + + let _ = syscall::write(*file, &mut env)?; + Ok(()) +} + +pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { + // Copy environment registers. + { + let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); + let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); + + let mut env_regs = syscall::EnvRegisters::default(); + let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; + let _ = syscall::write(*new_env_regs_fd, &env_regs)?; + } + + Ok(()) +} + +unsafe extern "C" fn fork_impl(initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp)) +} + +unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { + let _ = syscall::close(cur_filetable_fd); + let _ = syscall::close(new_pid_fd); +} + +asmfunction!(__relibc_internal_fork_wrapper -> usize: [" + stp x29, x30, [sp, #-16]! + stp x27, x28, [sp, #-16]! + stp x25, x26, [sp, #-16]! + stp x23, x24, [sp, #-16]! + stp x21, x22, [sp, #-16]! + stp x19, x20, [sp, #-16]! + + sub sp, sp, #32 + + //TODO: store floating point regs + + mov x0, sp + bl {fork_impl} + + add sp, sp, #32 + ldp x19, x20, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x29, x30, [sp], #16 + ret +"] <= [fork_impl = sym fork_impl]); + +asmfunction!(__relibc_internal_fork_ret: [" + ldp x0, x1, [sp] + bl {child_hook} + + //TODO: load floating point regs + + mov x0, xzr + + add sp, sp, #32 + ldp x19, x20, [sp], #16 + ldp x21, x22, [sp], #16 + ldp x23, x24, [sp], #16 + ldp x25, x26, [sp], #16 + ldp x27, x28, [sp], #16 + ldp x29, x30, [sp], #16 + + ret +"] <= [child_hook = sym child_hook]); + +asmfunction!(__relibc_internal_sigentry: [" + // old pc and x0 are saved in the sigcontrol struct + mrs x0, tpidr_el0 // ABI ptr + ldr x0, [x0] // TCB ptr + + // save x1-x3 and sp + stp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] + stp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] + mov x1, sp + str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}] + + sub x1, x1, 128 + and x1, x1, -16 + mov sp, x1 + + ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}] + + // load x1 and x2 with each word (tearing between x1 and x2 can occur) + // acquire ordering + add x2, x0, #{tcb_sc_off} + {sc_word} + ldaxp x1, x2, [x2] + + // reduce them by ANDing the upper and lower 32 bits + and x1, x1, x1, lsr #32 // put result in lo half + and x2, x2, x2, lsl #32 // put result in hi half + orr x1, x1, x2 // combine them into the set of pending unblocked + + // count trailing zeroes, to find signal bit + rbit x1, x1 + clz x1, x1 + mov x2, #32 + sub x1, x2, x1 + + // TODO: NOT ATOMIC! + add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action + ldxp x2, x3, [x2] + + // skip sigaltstack step if SA_ONSTACK is clear + // tbz x2, #57, 2f +2: + ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}] + ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}] + stp x2, x3, [sp], #-16 + + ldr x2, [x0, #{tcb_sa_off} + {sa_tmp_sp}] + mrs x3, nzcv + stp x2, x3, [sp], #-16 + + ldp x2, x3, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}] + stp x2, x3, [sp], #-16 + ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}] + stp x4, x3, [sp], #-16 + stp x6, x5, [sp], #-16 + stp x8, x7, [sp], #-16 + stp x10, x9, [sp], #-16 + stp x12, x11, [sp], #-16 + stp x14, x13, [sp], #-16 + stp x16, x15, [sp], #-16 + stp x18, x17, [sp], #-16 + stp x20, x19, [sp], #-16 + stp x22, x21, [sp], #-16 + stp x24, x23, [sp], #-16 + stp x26, x25, [sp], #-16 + stp x28, x27, [sp], #-16 + stp x30, x29, [sp], #-16 + + mov x0, sp + bl {inner} + + ldp x30, x29, [sp], #16 + ldp x28, x27, [sp], #16 + ldp x26, x25, [sp], #16 + ldp x24, x23, [sp], #16 + ldp x22, x21, [sp], #16 + ldp x20, x19, [sp], #16 + ldp x18, x17, [sp], #16 + ldp x16, x15, [sp], #16 + ldp x14, x13, [sp], #16 + ldp x12, x11, [sp], #16 + ldp x10, x9, [sp], #16 + ldp x8, x7, [sp], #16 + ldp x6, x5, [sp], #16 + ldp x4, x3, [sp], #16 + ldp x2, x1, [sp], #16 + ldr x0, [sp, #8] + msr nzcv, x0 + + // x18 is reserved by ABI as 'platform register', so clobbering it should be safe. + mov x18, sp + ldr x0, [sp], #16 + mov sp, x0 + mov x0, x18 + + ldp x18, x0, [x0] + br x18 +"] <= [ + tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)), + tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)), + sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2), + sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4), + sa_tmp_sp = const offset_of!(SigArea, tmp_sp), + sa_pctl = const offset_of!(SigArea, pctl), + sc_saved_pc = const offset_of!(Sigcontrol, saved_ip), + sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_word = const offset_of!(Sigcontrol, word), + inner = sym inner_c, +]); + +asmfunction!(__relibc_internal_rlct_clone_ret: [" + # Load registers + ldp x8, x0, [sp], #16 + ldp x1, x2, [sp], #16 + ldp x3, x4, [sp], #16 + + # Call entry point + blr x8 + + ret +"] <= []); + +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!("mov {}, sp", out(reg) sp); + } + sp +} + +pub unsafe fn manually_enter_trampoline() { + let ctl = &Tcb::current().unwrap().os_specific.control; + + ctl.saved_archdep_reg.set(0); + let ip_location = &ctl.saved_ip as *const _ as usize; + + core::arch::asm!(" + bl 2f + b 3f + 2: + str lr, [x0] + b __relibc_internal_sigentry + 3: + ", inout("x0") ip_location => _, out("lr") _); +} + +pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) {} diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs new file mode 100644 index 0000000000..80296b835a --- /dev/null +++ b/redox-rt/src/arch/i686.rs @@ -0,0 +1,281 @@ +use core::mem::offset_of; +use core::sync::atomic::Ordering; + +use syscall::*; + +use crate::proc::{fork_inner, FdGuard}; +use crate::signal::{inner_fastcall, PROC_CONTROL_STRUCT, RtSigarea, SigStack}; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub(crate) const STACK_TOP: usize = 1 << 31; +pub(crate) const STACK_SIZE: usize = 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub altstack_top: usize, + pub altstack_bottom: usize, + pub tmp_eip: usize, + pub tmp_esp: usize, + pub tmp_eax: usize, + pub tmp_edx: usize, + pub disable_signals_depth: u64, +} +#[derive(Debug, Default)] +#[repr(C, align(16))] +pub struct ArchIntRegs { + pub fxsave: [u16; 29], + + // ensure fxsave region is 16 byte aligned + pub _pad: [usize; 2], // fxsave "available" +0 + + pub ebp: usize, // fxsave "available" +8 + pub esi: usize, // avail +12 + pub edi: usize, // avail +16 + pub ebx: usize, // avail +20 + pub eax: usize, // avail +24 + pub ecx: usize, // avail +28 + pub edx: usize, // avail +32 + + pub eflags: usize, // avail +36 + pub eip: usize, // avail +40 + pub esp: usize, // avail +44 +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?); + + env.fsbase = 0; + env.gsbase = 0; + + let _ = syscall::write(*file, &mut env)?; + Ok(()) +} + +pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { + // Copy environment registers. + { + let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); + let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); + + let mut env_regs = syscall::EnvRegisters::default(); + let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; + let _ = syscall::write(*new_env_regs_fd, &env_regs)?; + } + + Ok(()) +} + +unsafe extern "cdecl" fn fork_impl(initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp)) +} + +unsafe extern "cdecl" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { + let _ = syscall::close(cur_filetable_fd); + let _ = syscall::close(new_pid_fd); +} + +asmfunction!(__relibc_internal_fork_wrapper -> usize: [" + push ebp + mov ebp, esp + + // Push preserved registers + push ebx + push edi + push esi + push ebp + + sub esp, 32 + + //TODO stmxcsr [esp+16] + fnstcw [esp+24] + + push esp + call {fork_impl} + pop esp + jmp 2f +"] <= [fork_impl = sym fork_impl]); + +asmfunction!(__relibc_internal_fork_ret: [" + // Arguments already on the stack + call {child_hook} + + //TODO ldmxcsr [esp+16] + fldcw [esp+24] + + xor eax, eax + + .p2align 4 +2: + add esp, 32 + + // Pop preserved registers + pop ebp + pop esi + pop edi + pop ebx + + pop ebp + ret +"] <= [child_hook = sym child_hook]); +asmfunction!(__relibc_internal_sigentry: [" + // Save some registers + mov gs:[{tcb_sa_off} + {sa_tmp_esp}], esp + mov gs:[{tcb_sa_off} + {sa_tmp_eax}], eax + mov gs:[{tcb_sa_off} + {sa_tmp_edx}], edx + + // Read pending half of first signal. This can be done nonatomically wrt the mask bits, since + // only this thread is allowed to modify the latter. + + // Read first signal word + mov eax, gs:[{tcb_sc_off} + {sc_word}] + and eax, gs:[{tcb_sc_off} + {sc_word} + 4] + bsf eax, eax + jnz 2f + + // Read second signal word + mov eax, gs:[{tcb_sc_off} + {sc_word} + 8] + and eax, gs:[{tcb_sc_off} + {sc_word} + 12] + bsf eax, eax + jz 7f + add eax, 32 +2: + and esp, -{STACK_ALIGN} + + mov edx, eax + add edx, edx + bt dword ptr [{pctl} + {pctl_off_actions} + edx * 8 + 4], 28 + jnc 4f + + mov edx, gs:[{tcb_sa_off} + {sa_altstack_top}] + cmp esp, edx + ja 3f + + cmp esp, gs:[{tcb_sa_off} + {sa_altstack_bottom}] + jnbe 4f +3: + mov esp, edx +4: + // Now that we have a stack, we can finally start populating the signal stack. + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esp}] + push dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}] + push dword ptr gs:[{tcb_sc_off} + {sc_saved_eflags}] + + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}] + push ecx + push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}] + push ebx + push edi + push esi + push ebp + + sub esp, 2 * 4 + 29 * 16 + fxsave [esp] + + push eax + sub esp, 3 * 4 + + mov ecx, esp + call {inner} + + fxrstor [esp + 16] + add esp, 16 + 29 * 16 + 2 * 4 + + pop ebp + pop esi + pop edi + pop ebx + pop eax + pop ecx + pop edx + + popfd + pop dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}] + + .globl __relibc_internal_sigentry_crit_first +__relibc_internal_sigentry_crit_first: + pop esp + + .globl __relibc_internal_sigentry_crit_second +__relibc_internal_sigentry_crit_second: + jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}] +7: + ud2 +"] <= [ + inner = sym inner_fastcall, + sa_tmp_eip = const offset_of!(SigArea, tmp_eip), + sa_tmp_esp = const offset_of!(SigArea, tmp_esp), + sa_tmp_eax = const offset_of!(SigArea, tmp_eax), + sa_tmp_edx = const offset_of!(SigArea, tmp_edx), + sa_altstack_top = const offset_of!(SigArea, altstack_top), + sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), + sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_saved_eip = const offset_of!(Sigcontrol, saved_ip), + sc_word = const offset_of!(Sigcontrol, word), + tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + pctl_off_actions = const offset_of!(SigProcControl, actions), + pctl = sym PROC_CONTROL_STRUCT, + STACK_ALIGN = const 16, +]); + +asmfunction!(__relibc_internal_rlct_clone_ret -> usize: [" + # Load registers + pop eax + + sub esp, 8 + + mov DWORD PTR [esp], 0x00001F80 + # TODO: ldmxcsr [esp] + mov WORD PTR [esp], 0x037F + fldcw [esp] + + add esp, 8 + + # Call entry point + call eax + + ret +"] <= []); +extern "C" { + fn __relibc_internal_sigentry_crit_first(); + fn __relibc_internal_sigentry_crit_second(); +} +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { + if stack.regs.eip == __relibc_internal_sigentry_crit_first as usize { + let stack_ptr = stack.regs.esp as *const usize; + stack.regs.esp = stack_ptr.read(); + stack.regs.eip = stack_ptr.sub(1).read(); + } else if stack.regs.eip == __relibc_internal_sigentry_crit_second as usize { + stack.regs.eip = area.tmp_eip; + } +} +pub unsafe fn manually_enter_trampoline() { + let c = &crate::Tcb::current().unwrap().os_specific.control; + c.control_flags.store(c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), Ordering::Release); + c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86? + + core::arch::asm!(" + call 2f + jmp 3f + 2: + pop dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}] + jmp __relibc_internal_sigentry + 3: + ", + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + sc_saved_eip = const offset_of!(Sigcontrol, saved_ip), + ); +} +/// Get current stack pointer, weak granularity guarantees. +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!("mov {}, esp", out(reg) sp); + } + sp +} diff --git a/src/platform/redox/redox-exec/src/arch/mod.rs b/redox-rt/src/arch/mod.rs similarity index 87% rename from src/platform/redox/redox-exec/src/arch/mod.rs rename to redox-rt/src/arch/mod.rs index 372b7802b9..efbef2b74b 100644 --- a/src/platform/redox/redox-exec/src/arch/mod.rs +++ b/redox-rt/src/arch/mod.rs @@ -4,9 +4,9 @@ pub use self::aarch64::*; pub mod aarch64; #[cfg(target_arch = "x86")] -pub use self::x86::*; +pub use self::i686::*; #[cfg(target_arch = "x86")] -pub mod x86; +pub mod i686; #[cfg(target_arch = "x86_64")] pub use self::x86_64::*; diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs new file mode 100644 index 0000000000..0b3ffa68db --- /dev/null +++ b/redox-rt/src/arch/x86_64.rs @@ -0,0 +1,402 @@ +use core::mem::offset_of; +use core::sync::atomic::{AtomicU8, Ordering}; + +use syscall::data::{Sigcontrol, SigProcControl}; +use syscall::error::*; +use syscall::flag::*; + +use crate::proc::{fork_inner, FdGuard}; +use crate::signal::{tmp_disable_signals, SigStack}; +use crate::signal::{inner_c, RtSigarea, PROC_CONTROL_STRUCT}; +use crate::Tcb; + +// Setup a stack starting from the very end of the address space, and then growing downwards. +pub(crate) const STACK_TOP: usize = 1 << 47; +pub(crate) const STACK_SIZE: usize = 1024 * 1024; + +#[derive(Debug, Default)] +#[repr(C)] +pub struct SigArea { + pub tmp_rip: usize, + pub tmp_rsp: usize, + pub tmp_rax: usize, + pub tmp_rdx: usize, + + pub altstack_top: usize, + pub altstack_bottom: usize, + pub disable_signals_depth: u64, + pub pctl: usize, // TODO: find out how to correctly reference that static +} + +#[repr(C, align(16))] +#[derive(Debug, Default)] +pub struct ArchIntRegs { + pub ymm_upper: [u128; 16], + pub fxsave: [u128; 29], + pub r15: usize, // fxsave "available" +0 + pub r14: usize, // available +8 + pub r13: usize, // available +16 + pub r12: usize, // available +24 + pub rbp: usize, // available +32 + pub rbx: usize, // available +40 + pub r11: usize, // outside fxsave, and so on + pub r10: usize, + pub r9: usize, + pub r8: usize, + pub rax: usize, + pub rcx: usize, + pub rdx: usize, + pub rsi: usize, + pub rdi: usize, + pub rflags: usize, + pub rip: usize, + pub rsp: usize, +} + +/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS +/// is already initialized as if it was a thread. +pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> { + let mut env = syscall::EnvRegisters::default(); + + let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?); + + env.fsbase = 0; + env.gsbase = 0; + + let _ = syscall::write(*file, &mut env)?; + Ok(()) +} + +pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { + // Copy environment registers. + { + let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); + let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); + + let mut env_regs = syscall::EnvRegisters::default(); + let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; + let _ = syscall::write(*new_env_regs_fd, &env_regs)?; + } + + Ok(()) +} + +unsafe extern "sysv64" fn fork_impl(initial_rsp: *mut usize) -> usize { + Error::mux(fork_inner(initial_rsp)) +} + +unsafe extern "sysv64" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) { + let _ = syscall::close(cur_filetable_fd); + let _ = syscall::close(new_pid_fd); +} + +asmfunction!(__relibc_internal_fork_wrapper -> usize: [" + push rbp + mov rbp, rsp + + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + + sub rsp, 32 + + stmxcsr [rsp+16] + fnstcw [rsp+24] + + mov rdi, rsp + call {fork_impl} + + add rsp, 80 + + pop rbp + ret + +"] <= [fork_impl = sym fork_impl]); +asmfunction!(__relibc_internal_fork_ret: [" + mov rdi, [rsp] + mov rsi, [rsp + 8] + call {child_hook} + + ldmxcsr [rsp+16] + fldcw [rsp+24] + + xor rax, rax + + add rsp, 32 + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + + pop rbp + ret +"] <= [child_hook = sym child_hook]); +asmfunction!(__relibc_internal_rlct_clone_ret: [" + # Load registers + pop rax + pop rdi + pop rsi + pop rdx + pop rcx + pop r8 + pop r9 + + sub rsp, 8 + + mov DWORD PTR [rsp], 0x00001F80 + ldmxcsr [rsp] + mov WORD PTR [rsp], 0x037F + fldcw [rsp] + + add rsp, 8 + + # Call entry point + call rax + + ret +"] <= []); + +asmfunction!(__relibc_internal_sigentry: [" + // Save some registers + mov fs:[{tcb_sa_off} + {sa_tmp_rsp}], rsp + mov fs:[{tcb_sa_off} + {sa_tmp_rax}], rax + mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx + + // First, select signal, always pick first available bit + + // Read first signal word + mov rax, fs:[{tcb_sc_off} + {sc_word}] + mov rdx, rax + shr rdx, 32 + and eax, edx + bsf eax, eax + jnz 2f + + // Read second signal word + mov rax, fs:[{tcb_sc_off} + {sc_word} + 8] + mov rdx, rax + shr rdx, 32 + and eax, edx + bsf eax, eax + jz 6f + add eax, 32 +2: + sub rsp, {REDZONE_SIZE} + and rsp, -{STACK_ALIGN} + + // By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or + // not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then + // skip the sigaltstack logic. + mov edx, eax + add edx, edx + lea rdx, [{pctl_off_actions} + edx * 8] + add rdx, fs:[{tcb_sa_off} + {sa_off_pctl}] + + // scale factor 16 doesn't exist, so we premultiplied edx by 2 + bt qword ptr [rdx], 56 + jnc 4f + + // Otherwise, the altstack is already active. The sigaltstack being disabled, is equivalent + // to setting 'top' to usize::MAX and 'bottom' to 0. + + // If current RSP is above altstack region, switch to altstack + mov rdx, fs:[{tcb_sa_off} + {sa_altstack_top}] + cmp rsp, rdx + cmova rsp, rdx + + // If current RSP is below altstack region, also switch to altstack + cmp rsp, fs:[{tcb_sa_off} + {sa_altstack_bottom}] + cmovbe rsp, rdx + + .p2align 4 +4: + // Now that we have a stack, we can finally start initializing the signal stack! + + push fs:[{tcb_sa_off} + {sa_tmp_rsp}] + push fs:[{tcb_sc_off} + {sc_saved_rip}] + push fs:[{tcb_sc_off} + {sc_saved_rflags}] + + push rdi + push rsi + push fs:[{tcb_sa_off} + {sa_tmp_rdx}] + push rcx + push fs:[{tcb_sa_off} + {sa_tmp_rax}] + push r8 + push r9 + push r10 + push r11 + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + sub rsp, (29 + 16) * 16 // fxsave region minus available bytes + fxsave64 [rsp + 16 * 16] + + // TODO: self-modifying? + cmp byte ptr [rip + {supports_avx}], 0 + je 5f + + // Prefer vextractf128 over vextracti128 since the former only requires AVX version 1. + vextractf128 [rsp + 15 * 16], ymm0, 1 + vextractf128 [rsp + 14 * 16], ymm1, 1 + vextractf128 [rsp + 13 * 16], ymm2, 1 + vextractf128 [rsp + 12 * 16], ymm3, 1 + vextractf128 [rsp + 11 * 16], ymm4, 1 + vextractf128 [rsp + 10 * 16], ymm5, 1 + vextractf128 [rsp + 9 * 16], ymm6, 1 + vextractf128 [rsp + 8 * 16], ymm7, 1 + vextractf128 [rsp + 7 * 16], ymm8, 1 + vextractf128 [rsp + 6 * 16], ymm9, 1 + vextractf128 [rsp + 5 * 16], ymm10, 1 + vextractf128 [rsp + 4 * 16], ymm11, 1 + vextractf128 [rsp + 3 * 16], ymm12, 1 + vextractf128 [rsp + 2 * 16], ymm13, 1 + vextractf128 [rsp + 16], ymm14, 1 + vextractf128 [rsp], ymm15, 1 +5: + push rax // selected signal + sub rsp, 8 + + mov rdi, rsp + call {inner} + + add rsp, 16 + + fxrstor64 [rsp] + + cmp byte ptr [rip + {supports_avx}], 0 + je 6f + + vinsertf128 ymm0, ymm0, [rsp + 15 * 16], 1 + vinsertf128 ymm1, ymm1, [rsp + 14 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 13 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 12 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 11 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 10 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 9 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 8 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 7 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 6 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 5 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 4 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 3 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 2 * 16], 1 + vinsertf128 ymm2, ymm2, [rsp + 16], 1 + vinsertf128 ymm2, ymm2, [rsp], 1 +6: + add rsp, (29 + 16) * 16 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + pop r11 + pop r10 + pop r9 + pop r8 + pop rax + pop rcx + pop rdx + pop rsi + pop rdi + + popfq + pop qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}] + + // x86 lacks atomic instructions for setting both the stack and instruction pointer + // simultaneously, except the slow microcoded IRETQ instruction. Thus, we let the arch_pre + // function emulate atomicity between the pop rsp and indirect jump. + + .globl __relibc_internal_sigentry_crit_first +__relibc_internal_sigentry_crit_first: + + pop rsp + + .globl __relibc_internal_sigentry_crit_second +__relibc_internal_sigentry_crit_second: + jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}] +6: + ud2 + // Spurious signal +"] <= [ + inner = sym inner_c, + sa_tmp_rip = const offset_of!(SigArea, tmp_rip), + sa_tmp_rsp = const offset_of!(SigArea, tmp_rsp), + sa_tmp_rax = const offset_of!(SigArea, tmp_rax), + sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx), + sa_altstack_top = const offset_of!(SigArea, altstack_top), + sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), + sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), + sc_saved_rip = const offset_of!(Sigcontrol, saved_ip), + sc_word = const offset_of!(Sigcontrol, word), + tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch), + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + pctl_off_actions = const offset_of!(SigProcControl, actions), + //pctl = sym PROC_CONTROL_STRUCT, + sa_off_pctl = const offset_of!(SigArea, pctl), + supports_avx = sym SUPPORTS_AVX, + REDZONE_SIZE = const 128, + STACK_ALIGN = const 16, +]); + +extern "C" { + fn __relibc_internal_sigentry_crit_first(); + fn __relibc_internal_sigentry_crit_second(); +} +pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) { + // It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is + // almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that + // signals are disabled in the prologue of the signal trampoline, which allows us to emulate + // atomicity inside the critical section, consisting of one instruction at 'crit_first', and + // one at 'crit_second', see asm. + + if stack.regs.rip == __relibc_internal_sigentry_crit_first as usize { + // Reexecute pop rsp and jump steps. This case needs to be different from the one below, + // since rsp has not been overwritten with the previous context's stack, just yet. At this + // point, we know [rsp+0] contains the saved RSP, and [rsp-8] contains the saved RIP. + let stack_ptr = stack.regs.rsp as *const usize; + stack.regs.rsp = stack_ptr.read(); + stack.regs.rip = stack_ptr.sub(1).read(); + } else if stack.regs.rip == __relibc_internal_sigentry_crit_second as usize { + // Almost finished, just reexecute the jump before tmp_rip is overwritten by this + // deeper-level signal. + stack.regs.rip = area.tmp_rip; + } +} + +static SUPPORTS_AVX: AtomicU8 = AtomicU8::new(1); // FIXME + +pub unsafe fn manually_enter_trampoline() { + let c = &Tcb::current().unwrap().os_specific.control; + c.control_flags.store(c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), Ordering::Release); + c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86? + + core::arch::asm!(" + lea rax, [rip + 2f] + mov fs:[{tcb_sc_off} + {sc_saved_rip}], rax + jmp __relibc_internal_sigentry + 2: + ", + out("rax") _, + tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control), + sc_saved_rip = const offset_of!(Sigcontrol, saved_ip), + ); +} + +/// Get current stack pointer, weak granularity guarantees. +pub fn current_sp() -> usize { + let sp: usize; + unsafe { + core::arch::asm!("mov {}, rsp", out(reg) sp); + } + sp +} diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs new file mode 100644 index 0000000000..8112c9b433 --- /dev/null +++ b/redox-rt/src/lib.rs @@ -0,0 +1,124 @@ +#![no_std] +#![feature(asm_const, array_chunks, int_roundings, let_chains, slice_ptr_get, sync_unsafe_cell)] +#![forbid(unreachable_patterns)] + +use generic_rt::{ExpectTlsFree, GenericTcb}; + +use self::signal::RtSigarea; + +extern crate alloc; + +#[macro_export] +macro_rules! asmfunction( + ($name:ident $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => { + ::core::arch::global_asm!(concat!(" + .p2align 4 + .section .text.", stringify!($name), ", \"ax\", @progbits + .globl ", stringify!($name), " + .type ", stringify!($name), ", @function + ", stringify!($name), ": + ", $($asmstmt, "\n",)* " + .size ", stringify!($name), ", . - ", stringify!($name), " + "), $($decl = $(sym $symname)?$(const $constval)?),*); + + extern "C" { + pub fn $name() $(-> $ret)?; + } + } +); + +pub mod arch; +pub mod proc; + +// TODO: Replace auxvs with a non-stack-based interface, but keep getauxval for compatibility +#[path = "../../src/platform/auxv_defs.rs"] +pub mod auxv_defs; + +pub mod signal; +pub mod sync; +pub mod sys; +pub mod thread; + +pub type Tcb = GenericTcb; + +/// OS and architecture specific code to activate TLS - Redox aarch64 +#[cfg(target_arch = "aarch64")] +pub unsafe fn tcb_activate(tls_end: usize, tls_len: usize) { + // Uses ABI page + let abi_ptr = tls_end - tls_len - 16; + core::ptr::write(abi_ptr as *mut usize, tls_end); + core::arch::asm!( + "msr tpidr_el0, {}", + in(reg) abi_ptr, + ); +} + +/// OS and architecture specific code to activate TLS - Redox x86 +#[cfg(target_arch = "x86")] +pub unsafe fn tcb_activate(tls_end: usize, _tls_len: usize) { + let mut env = syscall::EnvRegisters::default(); + + let file = syscall::open( + "thisproc:current/regs/env", + syscall::O_CLOEXEC | syscall::O_RDWR, + ) + .expect_notls("failed to open handle for process registers"); + + let _ = syscall::read(file, &mut env).expect_notls("failed to read gsbase"); + + env.gsbase = tls_end as u32; + + let _ = syscall::write(file, &env).expect_notls("failed to write gsbase"); + + let _ = syscall::close(file); +} + +/// OS and architecture specific code to activate TLS - Redox x86_64 +#[cfg(target_arch = "x86_64")] +pub unsafe fn tcb_activate(tls_end_and_tcb_start: usize, _tls_len: usize) { + let mut env = syscall::EnvRegisters::default(); + + let file = syscall::open( + "thisproc:current/regs/env", + syscall::O_CLOEXEC | syscall::O_RDWR, + ) + .expect_notls("failed to open handle for process registers"); + + let _ = syscall::read(file, &mut env).expect_notls("failed to read fsbase"); + + env.fsbase = tls_end_and_tcb_start as u64; + + let _ = syscall::write(file, &env).expect_notls("failed to write fsbase"); + + let _ = syscall::close(file); +} + +/// Initialize redox-rt in situations where relibc is not used +pub fn initialize_freestanding() { + // TODO: This code is a hack! Integrate the ld_so TCB code into generic-rt, and then use that + // (this function will need pointers to the ELF structs normally passed in auxvs), so the TCB + // is initialized properly. + + // TODO: TLS + let page = unsafe { + &mut *(syscall::fmap(!0, &syscall::Map { + offset: 0, + size: syscall::PAGE_SIZE, + flags: syscall::MapFlags::PROT_READ | syscall::MapFlags::PROT_WRITE | syscall::MapFlags::MAP_PRIVATE, + address: 0, + }).unwrap() as *mut Tcb) + }; + page.tcb_ptr = page; + page.tcb_len = syscall::PAGE_SIZE; + page.tls_end = (page as *mut Tcb).cast(); + + #[cfg(not(target_arch = "aarch64"))] + unsafe { + tcb_activate(page as *mut Tcb as usize, 0) + } + #[cfg(target_arch = "aarch64")] + unsafe { + let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr); + core::arch::asm!("msr tpidr_el0, {}", in(reg) abi_ptr); + } +} diff --git a/src/platform/redox/redox-exec/src/lib.rs b/redox-rt/src/proc.rs similarity index 93% rename from src/platform/redox/redox-exec/src/lib.rs rename to redox-rt/src/proc.rs index 17f4a64588..a89354f274 100644 --- a/src/platform/redox/redox-exec/src/lib.rs +++ b/redox-rt/src/proc.rs @@ -1,10 +1,5 @@ -#![no_std] -#![feature(array_chunks, int_roundings, let_chains, slice_ptr_get)] -#![forbid(unreachable_patterns)] - -extern crate alloc; - use core::mem::size_of; +use crate::{arch::*, auxv_defs::*}; use alloc::{boxed::Box, collections::BTreeMap, vec}; @@ -27,9 +22,6 @@ use syscall::{ PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE, }; -pub use self::arch::*; -mod arch; - pub enum FexecResult { Normal { addrspace_handle: FdGuard, @@ -429,20 +421,17 @@ where push(argc)?; - unsafe { - deactivate_tcb(*open_via_dup)?; + if let Ok(sighandler_fd) = syscall::dup(*open_via_dup, b"sighandler").map(FdGuard::new) { + let _ = syscall::write(*sighandler_fd, &SetSighandlerData { + user_handler: 0, + excp_handler: 0, + thread_control_addr: 0, + proc_control_addr: 0, + }); } - { - let current_sigaction_fd = FdGuard::new(syscall::dup(*open_via_dup, b"sigactions")?); - let empty_sigaction_fd = FdGuard::new(syscall::dup(*current_sigaction_fd, b"empty")?); - let sigaction_selection_fd = - FdGuard::new(syscall::dup(*open_via_dup, b"current-sigactions")?); - - let _ = syscall::write( - *sigaction_selection_fd, - &usize::to_ne_bytes(*empty_sigaction_fd), - )?; + unsafe { + deactivate_tcb(*open_via_dup)?; } // TODO: Restore old name if exec failed? @@ -713,55 +702,26 @@ pub fn create_set_addr_space_buf( buf } -#[path = "../../../auxv_defs.rs"] -pub mod auxv_defs; - -use auxv_defs::*; - /// Spawns a new context which will not share the same address space as the current one. File /// descriptors from other schemes are reobtained with `dup`, and grants referencing such file /// descriptors are reobtained through `fmap`. Other mappings are kept but duplicated using CoW. pub fn fork_impl() -> Result { - let mut old_mask = 0_u64; - syscall::sigprocmask(syscall::SIG_SETMASK, None, Some(&mut old_mask))?; + let mut old_mask = crate::signal::get_sigmask()?; let pid = unsafe { Error::demux(__relibc_internal_fork_wrapper())? }; if pid == 0 { - syscall::sigprocmask(syscall::SIG_SETMASK, Some(&old_mask), None)?; + crate::signal::set_sigmask(Some(old_mask), None)?; } Ok(pid) } -fn fork_inner(initial_rsp: *mut usize) -> Result { +pub fn fork_inner(initial_rsp: *mut usize) -> Result { let (cur_filetable_fd, new_pid_fd, new_pid); { let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); (new_pid_fd, new_pid) = new_context()?; - // Reuse the same sigaltstack and signal entry (all memory will be re-mapped CoW later). - { - let cur_sighandler_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sighandler")?); - let new_sighandler_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sighandler")?); - - let mut sighandler_buf = SetSighandlerData::default(); - - let _ = syscall::read(*cur_sighandler_fd, &mut sighandler_buf)?; - let _ = syscall::write(*new_sighandler_fd, &sighandler_buf)?; - } - - // Reuse the same sigactions (by value). - { - let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?); - let new_sigaction_fd = FdGuard::new(syscall::dup(*cur_sigaction_fd, b"copy")?); - let new_sigaction_sel_fd = - FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?); - - let _ = syscall::write( - *new_sigaction_sel_fd, - &usize::to_ne_bytes(*new_sigaction_fd), - )?; - } copy_str(*cur_pid_fd, *new_pid_fd, "name")?; // Copy existing files into new file table, but do not reuse the same file table (i.e. new @@ -808,7 +768,7 @@ fn fork_inner(initial_rsp: *mut usize) -> Result { continue; } - let mut buf; + let buf; // TODO: write! using some #![no_std] Cursor type (tracking the length)? #[cfg(target_pointer_width = "64")] @@ -851,6 +811,17 @@ fn fork_inner(initial_rsp: *mut usize) -> Result { ); let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; } + { + // Reuse the same sigaltstack and signal entry (all memory will be re-mapped CoW later). + // + // Do this after the address space is cloned, since the kernel will get a shared + // reference to the TCB and whatever pages stores the signal proc control struct. + { + let new_sighandler_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sighandler")?); + let _ = syscall::write(*new_sighandler_fd, &crate::signal::current_setsighandler_struct())?; + } + + } copy_env_regs(*cur_pid_fd, *new_pid_fd)?; } // Copy the file table. We do this last to ensure that all previously used file descriptors are diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs new file mode 100644 index 0000000000..3c21bde1a3 --- /dev/null +++ b/redox-rt/src/signal.rs @@ -0,0 +1,496 @@ +use core::cell::{Cell, UnsafeCell}; +use core::ffi::c_int; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use syscall::{RawAction, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGXCPU, SIGXFSZ}; +use syscall::{Error, Result, SetSighandlerData, SigProcControl, Sigcontrol, SigcontrolFlags, EINVAL, SIGCHLD, SIGCONT, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH, data::AtomicU64}; + +use crate::{arch::*, Tcb}; +use crate::sync::Mutex; + +#[cfg(target_arch = "x86_64")] +static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); + +pub fn sighandler_function() -> usize { + // TODO: HWCAP? + + __relibc_internal_sigentry as usize +} + +#[repr(C)] +pub struct SigStack { + #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] + _pad: [usize; 1], // pad to 16 bytes alignment + + #[cfg(target_arch = "x86")] + _pad: [usize; 3], // pad to 16 bytes alignment + + sig_num: usize, + + // x86_64: 864 bytes + // i686: 512 bytes + // aarch64: 272 bytes (SIMD TODO) + pub regs: ArchIntRegs, +} + +#[inline(always)] +unsafe fn inner(stack: &mut SigStack) { + let os = &Tcb::current().unwrap().os_specific; + + let sig_idx = stack.sig_num; + + // Commenting out this line will stress test how the signal code responds to 'interrupt spraying'. + os.control.word[sig_idx / 32].fetch_and(!(1 << (sig_idx % 32)), Ordering::Relaxed); + + // asm counts from 0 + stack.sig_num += 1; + + arch_pre(stack, &mut *os.arch.get()); + + let sigaction = { + let mut guard = SIGACTIONS_LOCK.lock(); + let action = convert_old(&PROC_CONTROL_STRUCT.actions[stack.sig_num - 1]); + if action.flags.contains(SigactionFlags::RESETHAND) { + // TODO: other things that must be set + drop(guard); + sigaction(stack.sig_num as u8, Some(&Sigaction { + kind: SigactionKind::Default, + mask: 0, + flags: SigactionFlags::empty(), + }), None); + } + action + }; + + let handler = match sigaction.kind { + SigactionKind::Ignore => { + panic!("ctl {:x?} signal {}", os.control, stack.sig_num) + } + SigactionKind::Default => { + syscall::exit(stack.sig_num); + unreachable!(); + } + SigactionKind::Handled { handler } => handler, + }; + + // Set sigmask to sa_mask and unmark the signal as pending. + let prev_sigallow_lo = os.control.word[0].load(Ordering::Relaxed) >> 32; + let prev_sigallow_hi = os.control.word[1].load(Ordering::Relaxed) >> 32; + let prev_sigallow = prev_sigallow_lo | (prev_sigallow_hi << 32); + + let mut sigallow_inside = !sigaction.mask & prev_sigallow; + if !sigaction.flags.contains(SigactionFlags::NODEFER) { + sigallow_inside &= !sig_bit(stack.sig_num); + } + let sigallow_inside_lo = sigallow_inside & 0xffff_ffff; + let sigallow_inside_hi = sigallow_inside >> 32; + + //let _ = syscall::write(1, &alloc::format!("WORD0 {:x?}\n", os.control.word).as_bytes()); + let prev_w0 = os.control.word[0].fetch_add((sigallow_inside_lo << 32).wrapping_sub(prev_sigallow_lo << 32), Ordering::Relaxed); + let prev_w1 = os.control.word[1].fetch_add((sigallow_inside_hi << 32).wrapping_sub(prev_sigallow_hi << 32), Ordering::Relaxed); + //let _ = syscall::write(1, &alloc::format!("WORD1 {:x?}\n", os.control.word).as_bytes()); + + // TODO: If sa_mask caused signals to be unblocked, deliver one or all of those first? + + // Re-enable signals again. + let control_flags = &os.control.control_flags; + control_flags.store(control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(), Ordering::Release); + core::sync::atomic::compiler_fence(Ordering::Acquire); + + // Call handler, either sa_handler or sa_siginfo depending on flag. + if sigaction.flags.contains(SigactionFlags::SIGINFO) && let Some(sigaction) = handler.sigaction { + //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes()); + sigaction(stack.sig_num as c_int, core::ptr::null_mut(), core::ptr::null_mut()); + } else if let Some(handler) = handler.handler { + //let _ = syscall::write(1, alloc::format!("HANDLER {:p}\n", handler).as_bytes()); + handler(stack.sig_num as c_int); + } + //let _ = syscall::write(1, alloc::format!("RETURNED HANDLER\n").as_bytes()); + + // Disable signals while we modify the sigmask again + control_flags.store(control_flags.load(Ordering::Relaxed) | SigcontrolFlags::INHIBIT_DELIVERY.bits(), Ordering::Release); + core::sync::atomic::compiler_fence(Ordering::Acquire); + + // Update allowset again. + //let _ = syscall::write(1, &alloc::format!("WORD2 {:x?}\n", os.control.word).as_bytes()); + + let prev_w0 = os.control.word[0].fetch_add((prev_sigallow_lo << 32).wrapping_sub(sigallow_inside_lo << 32), Ordering::Relaxed); + let prev_w1 = os.control.word[1].fetch_add((prev_sigallow_hi << 32).wrapping_sub(sigallow_inside_hi << 32), Ordering::Relaxed); + //let _ = syscall::write(1, &alloc::format!("WORD3 {:x?}\n", os.control.word).as_bytes()); + + // TODO: If resetting the sigmask caused signals to be unblocked, then should they be delivered + // here? And would it be possible to tail-call-optimize that? + + //let _ = syscall::write(1, alloc::format!("will return to {:x?}\n", stack.regs.eip).as_bytes()); + + // And re-enable them again + control_flags.store(control_flags.load(Ordering::Relaxed) & !SigcontrolFlags::INHIBIT_DELIVERY.bits(), Ordering::Release); + core::sync::atomic::compiler_fence(Ordering::Acquire); +} +#[cfg(not(target_arch = "x86"))] +pub(crate) unsafe extern "C" fn inner_c(stack: usize) { + inner(&mut *(stack as *mut SigStack)) +} +#[cfg(target_arch = "x86")] +pub(crate) unsafe extern "fastcall" fn inner_fastcall(stack: usize) { + inner(&mut *(stack as *mut SigStack)) +} + +pub fn get_sigmask() -> Result { + let mut mask = 0; + modify_sigmask(Some(&mut mask), Option:: u32>::None)?; + Ok(mask) +} +pub fn set_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { + modify_sigmask(old, new.map(move |newmask| move |_, upper| if upper { newmask >> 32 } else { newmask } as u32)) +} +pub fn or_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { + // Parsing nightmare... :) + modify_sigmask(old, new.map(move |newmask| move |oldmask, upper| oldmask | if upper { newmask >> 32 } else { newmask } as u32)) +} +pub fn andn_sigmask(new: Option, old: Option<&mut u64>) -> Result<()> { + modify_sigmask(old, new.map(move |newmask| move |oldmask, upper| oldmask & !if upper { newmask >> 32 } else { newmask } as u32)) +} +fn modify_sigmask(old: Option<&mut u64>, op: Option u32>) -> Result<()> { + let _guard = tmp_disable_signals(); + let ctl = current_sigctl(); + + let mut words = ctl.word.each_ref().map(|w| w.load(Ordering::Relaxed)); + + if let Some(old) = old { + *old = !combine_allowset(words); + } + let Some(mut op) = op else { + return Ok(()); + }; + + let mut can_raise = 0; + let mut cant_raise = 0; + + //let _ = syscall::write(1, &alloc::format!("OLDWORD {:x?}\n", ctl.word).as_bytes()); + for i in 0..2 { + let old_allow_bits = words[i] & 0xffff_ffff_0000_0000; + let new_allow_bits = u64::from(!op(!((old_allow_bits >> 32) as u32), i == 1)) << 32; + + ctl.word[i].fetch_add(new_allow_bits.wrapping_sub(old_allow_bits), Ordering::Relaxed); + } + //let _ = syscall::write(1, &alloc::format!("NEWWORD {:x?}\n", ctl.word).as_bytes()); + + // TODO: Prioritize cant_raise realtime signals? + + Ok(()) +} + +#[derive(Clone, Copy, Default)] +pub enum SigactionKind { + #[default] + Default, + + Ignore, + Handled { + handler: SignalHandler, + }, +} + +#[derive(Clone, Copy, Default)] +pub struct Sigaction { + pub kind: SigactionKind, + pub mask: u64, + pub flags: SigactionFlags, +} + +impl Sigaction { + fn ip(&self) -> usize { + unsafe { + match self.kind { + SigactionKind::Handled { handler } => if self.flags.contains(SigactionFlags::SIGINFO) { + handler.sigaction.map_or(0, |a| a as usize) + } else { + handler.handler.map_or(0, |a| a as usize) + } + _ => 0, + } + } + } +} + +const MASK_DONTCARE: u64 = !0; + +fn convert_old(action: &RawAction) -> Sigaction { + let old_first = action.first.load(Ordering::Relaxed); + let old_mask = action.user_data.load(Ordering::Relaxed); + + let handler = (old_first & !(u64::from(STORED_FLAGS) << 32)) as usize; + let flags = SigactionFlags::from_bits_retain(((old_first >> 32) as u32) & STORED_FLAGS); + + let kind = if handler == default_handler as usize { + SigactionKind::Default + } else if flags.contains(SigactionFlags::IGNORED) { + SigactionKind::Ignore + } else { + SigactionKind::Handled { handler: unsafe { core::mem::transmute(handler as usize) } } + }; + + Sigaction { + mask: old_mask, + flags, + kind, + } +} + +pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction>) -> Result<()> { + if matches!(usize::from(signal), 0 | 32 | SIGKILL | SIGSTOP | 65..) { + return Err(Error::new(EINVAL)); + } + + let _sigguard = tmp_disable_signals(); + let ctl = current_sigctl(); + + let _guard = SIGACTIONS_LOCK.lock(); + + let action = &PROC_CONTROL_STRUCT.actions[usize::from(signal) - 1]; + + if let Some(old) = old { + *old = convert_old(action); + } + + let Some(new) = new else { + return Ok(()); + }; + + let explicit_handler = new.ip(); + + let (mask, flags, handler) = match (usize::from(signal), new.kind) { + (_, SigactionKind::Ignore) | (SIGURG | SIGWINCH, SigactionKind::Default) => { + // TODO: POSIX specifies that pending signals shall be discarded if set to SIG_IGN by + // sigaction. + // TODO: handle tmp_disable_signals + (MASK_DONTCARE, SigactionFlags::IGNORED, if matches!(new.kind, SigactionKind::Default) { + default_handler as usize + } else { + 0 + }) + } + // TODO: Handle pending signals before these flags are set. + (SIGTSTP | SIGTTOU | SIGTTIN, SigactionKind::Default) => (MASK_DONTCARE, SigactionFlags::SIG_SPECIFIC, default_handler as usize), + (SIGCHLD, SigactionKind::Default) => { + let nocldstop_bit = new.flags & SigactionFlags::SIG_SPECIFIC; + (MASK_DONTCARE, SigactionFlags::IGNORED | nocldstop_bit, default_handler as usize) + } + + (_, SigactionKind::Default) => { + (new.mask, new.flags, default_handler as usize) + }, + (_, SigactionKind::Handled { .. }) => { + (new.mask, new.flags, explicit_handler) + } + }; + let new_first = (handler as u64) | (u64::from(flags.bits() & STORED_FLAGS) << 32); + action.first.store(new_first, Ordering::Relaxed); + action.user_data.store(mask, Ordering::Relaxed); + + Ok(()) +} + +fn current_sigctl() -> &'static Sigcontrol { + &unsafe { Tcb::current() }.unwrap().os_specific.control +} + +pub struct TmpDisableSignalsGuard { _inner: () } + +pub fn tmp_disable_signals() -> TmpDisableSignalsGuard { + unsafe { + let ctl = ¤t_sigctl().control_flags; + ctl.store(ctl.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(), Ordering::Release); + core::sync::atomic::compiler_fence(Ordering::Acquire); + + // TODO: fence? + (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth += 1; + } + + TmpDisableSignalsGuard { _inner: () } +} +impl Drop for TmpDisableSignalsGuard { + fn drop(&mut self) { + unsafe { + let depth = &mut (*Tcb::current().unwrap().os_specific.arch.get()).disable_signals_depth; + *depth -= 1; + + if *depth == 0 { + let ctl = ¤t_sigctl().control_flags; + ctl.store(ctl.load(Ordering::Relaxed) & !syscall::flag::INHIBIT_DELIVERY.bits(), Ordering::Release); + core::sync::atomic::compiler_fence(Ordering::Acquire); + } + } + } +} + +bitflags::bitflags! { + // Some flags are ignored by the rt, but they match relibc's 1:1 to simplify conversion. + #[derive(Clone, Copy, Default)] + pub struct SigactionFlags: u32 { + const NOCLDWAIT = 2; + const RESTORER = 4; + const SIGINFO = 0x0200_0000; + const ONSTACK = 0x0400_0000; + const RESTART = 0x0800_0000; + const NODEFER = 0x1000_0000; + const RESETHAND = 0x2000_0000; + const SIG_SPECIFIC = 0x4000_0000; + const IGNORED = 0x8000_0000; + } +} + +const STORED_FLAGS: u32 = 0xfe00_0000; + +fn default_handler(sig: c_int) { + syscall::exit(sig as usize); +} + +#[derive(Clone, Copy)] +pub union SignalHandler { + pub handler: Option, + pub sigaction: Option, +} + +static SIGACTIONS_LOCK: Mutex<()> = Mutex::new(()); + +pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl { + pending: AtomicU64::new(0), + actions: [const { + RawAction { + first: AtomicU64::new(0), + user_data: AtomicU64::new(0), + } + }; 64], +}; + +fn combine_allowset([lo, hi]: [u64; 2]) -> u64 { + (lo >> 32) | ((hi >> 32) << 32) +} + +const fn sig_bit(sig: usize) -> u64 { + //assert_ne!(sig, 32); + //assert_ne!(sig, 0); + 1 << (sig - 1) +} + +pub fn setup_sighandler(area: &RtSigarea) { + { + let mut sigactions = SIGACTIONS_LOCK.lock(); + for (sig_idx, action) in PROC_CONTROL_STRUCT.actions.iter().enumerate() { + let sig = sig_idx + 1; + let bits = if matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU) { + SigactionFlags::SIG_SPECIFIC + } else if matches!(sig, SIGCHLD | SIGURG | SIGWINCH) { + SigactionFlags::IGNORED + } else { + SigactionFlags::empty() + }; + action.first.store((u64::from(bits.bits()) << 32) | default_handler as u64, Ordering::Relaxed); + } + } + let arch = unsafe { &mut *area.arch.get() }; + { + // The asm decides whether to use the altstack, based on whether the saved stack pointer + // was already on that stack. Thus, setting the altstack to the entire address space, is + // equivalent to not using any altstack at all (the default). + arch.altstack_top = usize::MAX; + arch.altstack_bottom = 0; + #[cfg(not(target_arch = "x86"))] + { + arch.pctl = core::ptr::addr_of!(PROC_CONTROL_STRUCT) as usize; + } + } + + #[cfg(target_arch = "x86_64")] + { + let cpuid_eax1_ecx = unsafe { core::arch::x86_64::__cpuid(1) }.ecx; + CPUID_EAX1_ECX.store(cpuid_eax1_ecx, core::sync::atomic::Ordering::Relaxed); + } + + let data = current_setsighandler_struct(); + + let fd = syscall::open( + "thisproc:current/sighandler", + syscall::O_WRONLY | syscall::O_CLOEXEC, + ) + .expect("failed to open thisproc:current/sighandler"); + syscall::write(fd, &data).expect("failed to write to thisproc:current/sighandler"); + let _ = syscall::close(fd); + + // TODO: Inherited set of ignored signals + set_sigmask(Some(0), None); +} +#[derive(Debug, Default)] +pub struct RtSigarea { + pub control: Sigcontrol, + pub arch: UnsafeCell, +} +pub fn current_setsighandler_struct() -> SetSighandlerData { + SetSighandlerData { + user_handler: sighandler_function(), + excp_handler: 0, // TODO + thread_control_addr: core::ptr::addr_of!(unsafe { Tcb::current() }.unwrap().os_specific.control) as usize, + proc_control_addr: &PROC_CONTROL_STRUCT as *const SigProcControl as usize, + } +} + +#[derive(Clone, Copy, Default, PartialEq)] +pub enum Sigaltstack { + #[default] + Disabled, + + Enabled { onstack: bool, base: *mut (), size: usize }, +} +pub unsafe fn sigaltstack(new: Option<&Sigaltstack>, old_out: Option<&mut Sigaltstack>) -> Result<()> { + let _g = tmp_disable_signals(); + let tcb = &mut *Tcb::current().unwrap().os_specific.arch.get(); + + let old = if tcb.altstack_bottom == 0 && tcb.altstack_top == usize::MAX { + Sigaltstack::Disabled + } else { + Sigaltstack::Enabled { + base: tcb.altstack_bottom as *mut (), + size: tcb.altstack_top - tcb.altstack_bottom, + onstack: (tcb.altstack_bottom..tcb.altstack_top).contains(&crate::arch::current_sp()), + } + }; + + if matches!(old, Sigaltstack::Enabled { onstack: true, .. }) && new != Some(&old) { + return Err(Error::new(EPERM)); + } + + if let Some(old_out) = old_out { + *old_out = old; + } + if let Some(new) = new { + match *new { + Sigaltstack::Disabled => { + tcb.altstack_bottom = 0; + tcb.altstack_top = usize::MAX; + } + Sigaltstack::Enabled { onstack: true, .. } => return Err(Error::new(EINVAL)), + Sigaltstack::Enabled { base, size, onstack: false } => { + if size < MIN_SIGALTSTACK_SIZE { + return Err(Error::new(ENOMEM)); + } + + tcb.altstack_bottom = base as usize; + tcb.altstack_top = base as usize + size; + } + } + } + Ok(()) +} + +pub const MIN_SIGALTSTACK_SIZE: usize = 8192; + +pub fn currently_pending() -> u64 { + let control = &unsafe { Tcb::current().unwrap() }.os_specific.control; + let w0 = control.word[0].load(Ordering::Relaxed); + let w1 = control.word[1].load(Ordering::Relaxed); + let pending_blocked_lo = w0 & !(w0 >> 32); + let pending_unblocked_hi = w1 & !(w0 >> 32); + pending_blocked_lo | (pending_unblocked_hi << 32) +} diff --git a/redox-rt/src/sync.rs b/redox-rt/src/sync.rs new file mode 100644 index 0000000000..172bd37b1f --- /dev/null +++ b/redox-rt/src/sync.rs @@ -0,0 +1,52 @@ +// TODO: Share code for simple futex-based mutex between relibc's Mutex<()> and this. + +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use core::sync::atomic::{AtomicU32, Ordering}; + +pub struct Mutex { + pub lockword: AtomicU32, + pub inner: UnsafeCell, +} + +const UNLOCKED: u32 = 0; +const LOCKED: u32 = 1; +const WAITING: u32 = 2; + +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} + +impl Mutex { + pub const fn new(t: T) -> Self { + Self { + lockword: AtomicU32::new(0), + inner: UnsafeCell::new(t), + } + } + pub fn lock(&self) -> MutexGuard<'_, T> { + while self.lockword.compare_exchange(UNLOCKED, LOCKED, Ordering::Acquire, Ordering::Relaxed).is_err() { + core::hint::spin_loop(); + } + MutexGuard { lock: self } + } +} +pub struct MutexGuard<'l, T> { + lock: &'l Mutex, +} +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.inner.get() } + } +} +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.inner.get() } + } +} +impl Drop for MutexGuard<'_, T> { + fn drop(&mut self) { + self.lock.lockword.store(UNLOCKED, Ordering::Release); + } +} diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs new file mode 100644 index 0000000000..8cc3a0dbc1 --- /dev/null +++ b/redox-rt/src/sys.rs @@ -0,0 +1,43 @@ +use syscall::error::{Result, Error, EINTR}; + +use crate::arch::manually_enter_trampoline; +use crate::signal::tmp_disable_signals; + +#[inline] +fn wrapper(mut f: impl FnMut() -> Result) -> Result { + loop { + let res = f(); + + if res == Err(Error::new(EINTR)) { + unsafe { + manually_enter_trampoline(); + } + } + + return res; + } +} + +// TODO: uninitialized memory? +#[inline] +pub fn posix_read(fd: usize, buf: &mut [u8]) -> Result { + wrapper(|| syscall::read(fd, buf)) +} +#[inline] +pub fn posix_write(fd: usize, buf: &[u8]) -> Result { + wrapper(|| syscall::write(fd, buf)) +} +#[inline] +pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { + match wrapper(|| syscall::kill(pid, sig)) { + Ok(_) | Err(Error { errno: EINTR }) => Ok(()), + Err(error) => Err(error), + } +} +#[inline] +pub fn posix_killpg(pgrp: usize, sig: usize) -> Result<()> { + match wrapper(|| syscall::kill(usize::wrapping_neg(pgrp), sig)) { + Ok(_) | Err(Error { errno: EINTR }) => Ok(()), + Err(error) => Err(error), + } +} diff --git a/redox-rt/src/thread.rs b/redox-rt/src/thread.rs new file mode 100644 index 0000000000..9b348a62d7 --- /dev/null +++ b/redox-rt/src/thread.rs @@ -0,0 +1,49 @@ +use syscall::SetSighandlerData; +use syscall::{Result, O_CLOEXEC}; + +use crate::arch::*; +use crate::proc::*; +use crate::signal::sighandler_function; + +/// Spawns a new context sharing the same address space as the current one (i.e. a new thread). +pub unsafe fn rlct_clone_impl(stack: *mut usize) -> Result { + let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); + let (new_pid_fd, new_pid) = new_context()?; + + copy_str(*cur_pid_fd, *new_pid_fd, "name")?; + + // Inherit existing address space + { + let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); + let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); + + let buf = create_set_addr_space_buf( + *cur_addr_space_fd, + __relibc_internal_rlct_clone_ret as usize, + stack as usize, + ); + let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; + } + + // Inherit reference to file table + { + let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); + let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); + + let _ = syscall::write( + *new_filetable_sel_fd, + &usize::to_ne_bytes(*cur_filetable_fd), + )?; + } + + // Since the signal handler is not yet initialized, signals specifically targeting the thread + // (relibc is only required to implement thread-specific signals that already originate from + // the same process) will be discarded. Process-specific signals will ignore this new thread, + // until it has initialized its own signal handler. + + // Unblock context. + let start_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"start")?); + let _ = syscall::write(*start_fd, &[0])?; + + Ok(new_pid) +} diff --git a/src/header/signal/cbindgen.toml b/src/header/signal/cbindgen.toml index a7055988c9..3c79a66073 100644 --- a/src/header/signal/cbindgen.toml +++ b/src/header/signal/cbindgen.toml @@ -1,4 +1,4 @@ -sys_includes = ["stdint.h", "sys/types.h", "time.h", "bits/pthread.h"] +sys_includes = ["bits/signal.h", "stdint.h", "sys/types.h", "time.h", "bits/pthread.h"] include_guard = "_RELIBC_SIGNAL_H" trailer = "#include " language = "C" @@ -15,3 +15,4 @@ prefix_with_name = true [export.rename] "timespec" = "struct timespec" +"sigaction" = "struct sigaction" diff --git a/src/header/signal/linux.rs b/src/header/signal/linux.rs index de013cdc13..fb23a0e99b 100644 --- a/src/header/signal/linux.rs +++ b/src/header/signal/linux.rs @@ -58,7 +58,8 @@ pub const SIGSYS: usize = 31; pub const SIGUNUSED: usize = SIGSYS; pub const NSIG: usize = 32; -pub const SIGRTMIN: usize = 34; +pub const SIGRTMIN: usize = 35; // TODO: decrease to 34 +pub const SIGRTMAX: usize = 64; pub const SA_NOCLDSTOP: usize = 1; pub const SA_NOCLDWAIT: usize = 2; diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index 5e8d52063a..01cd57d7eb 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -7,11 +7,13 @@ use cbitset::BitSet; use crate::{ header::{errno, time::timespec}, platform::{self, types::*, Pal, PalSignal, Sys}, - pthread, + pthread::{self, Errno, ResultExt}, }; pub use self::sys::*; +use super::errno::EFAULT; + #[cfg(target_os = "linux")] #[path = "linux.rs"] pub mod sys; @@ -33,6 +35,7 @@ pub const SIG_SETMASK: c_int = 2; #[repr(C)] #[derive(Clone, Debug)] +/// cbindgen:ignore pub struct sigaction { pub sa_handler: Option, pub sa_flags: c_ulong, @@ -50,7 +53,7 @@ pub struct sigaltstack { #[repr(C)] #[derive(Clone, Debug)] -pub struct siginfo_t { +pub struct siginfo { pub si_signo: c_int, pub si_errno: c_int, pub si_code: c_int, @@ -58,7 +61,10 @@ pub struct siginfo_t { _si_align: [usize; 0], } +/// cbindgen:ignore pub type sigset_t = c_ulonglong; +/// cbindgen:ignore +pub type siginfo_t = siginfo; pub type stack_t = sigaltstack; @@ -107,17 +113,13 @@ pub unsafe extern "C" fn sigaction( act: *const sigaction, oact: *mut sigaction, ) -> c_int { - let act_opt = act.as_ref().map(|act| { - let mut act_clone = act.clone(); - act_clone.sa_flags |= SA_RESTORER as c_ulong; - act_clone.sa_restorer = Some(__restore_rt); - act_clone - }); - Sys::sigaction(sig, act_opt.as_ref(), oact.as_mut()) + Sys::sigaction(sig, act.as_ref(), oact.as_mut()) + .map(|()| 0) + .or_minus_one_errno() } #[no_mangle] -pub extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int { +pub unsafe extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int { if signo <= 0 || signo as usize > NSIG { platform::ERRNO.set(errno::EINVAL); return -1; @@ -131,20 +133,13 @@ pub extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int { #[no_mangle] pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int { - if !ss.is_null() { - if (*ss).ss_flags != SS_DISABLE as c_int { - return errno::EINVAL; - } - if (*ss).ss_size < MINSIGSTKSZ { - return errno::ENOMEM; - } - } - - Sys::sigaltstack(ss, old_ss) + Sys::sigaltstack(ss.as_ref(), old_ss.as_mut()) + .map(|()| 0) + .or_minus_one_errno() } #[no_mangle] -pub extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int { +pub unsafe extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int { if signo <= 0 || signo as usize > NSIG { platform::ERRNO.set(errno::EINVAL); return -1; @@ -222,11 +217,6 @@ pub unsafe extern "C" fn sigismember(set: *const sigset_t, signo: c_int) -> c_in 0 } -extern "C" { - // Defined in assembly inside platform/x/mod.rs - fn __restore_rt(); -} - #[no_mangle] pub extern "C" fn signal( sig: c_int, @@ -234,8 +224,8 @@ pub extern "C" fn signal( ) -> Option { let sa = sigaction { sa_handler: func, - sa_flags: SA_RESTART as c_ulong, - sa_restorer: Some(__restore_rt), + sa_flags: SA_RESTART as _, + sa_restorer: None, // set by platform if applicable sa_mask: sigset_t::default(), }; let mut old_sa = mem::MaybeUninit::uninit(); @@ -257,7 +247,9 @@ pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int { #[no_mangle] pub unsafe extern "C" fn sigpending(set: *mut sigset_t) -> c_int { - Sys::sigpending(set) + (|| Sys::sigpending(set.as_mut().ok_or(Errno(EFAULT))?))() + .map(|()| 0) + .or_minus_one_errno() } const BELOW_SIGRTMIN_MASK: sigset_t = (1 << SIGRTMIN) - 1; @@ -270,14 +262,23 @@ pub unsafe extern "C" fn sigprocmask( set: *const sigset_t, oset: *mut sigset_t, ) -> c_int { - let set = set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK); + (|| { + let set = set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK); + let mut oset = oset.as_mut(); - Sys::sigprocmask( - how, - set.as_ref() - .map_or(core::ptr::null(), |r| r as *const sigset_t), - oset, - ) + Sys::sigprocmask( + how, + set.as_ref(), + oset.as_deref_mut(), // as_deref_mut for lifetime reasons + )?; + + if let Some(oset) = oset { + *oset &= !RLCT_SIGNAL_MASK; + } + + Ok(0) + })() + .or_minus_one_errno() } #[no_mangle] @@ -316,7 +317,7 @@ pub unsafe extern "C" fn sigset( let mut sa = sigaction { sa_handler: func, sa_flags: 0 as c_ulong, - sa_restorer: Some(__restore_rt), + sa_restorer: None, // set by platform if applicable sa_mask: sigset_t::default(), }; sigemptyset(&mut sa.sa_mask); diff --git a/src/header/signal/redox.rs b/src/header/signal/redox.rs index b4e8496add..689b9da3bf 100644 --- a/src/header/signal/redox.rs +++ b/src/header/signal/redox.rs @@ -1,38 +1,5 @@ use core::arch::global_asm; -// x8 is register, 119 is SIGRETURN -#[cfg(target_arch = "aarch64")] -global_asm!( - " - .global __restore_rt - __restore_rt: - mov x8, #119 - svc 0 -" -); -// Needs to be defined in assembly because it can't have a function prologue -// eax is register, 119 is SIGRETURN -#[cfg(target_arch = "x86")] -global_asm!( - " - .global __restore_rt - __restore_rt: - mov eax, 119 - int 0x80 -" -); -// Needs to be defined in assembly because it can't have a function prologue -// rax is register, 119 is SIGRETURN -#[cfg(target_arch = "x86_64")] -global_asm!( - " - .global __restore_rt - __restore_rt: - mov rax, 119 - syscall -" -); - pub const SIGHUP: usize = 1; pub const SIGINT: usize = 2; pub const SIGQUIT: usize = 3; @@ -66,17 +33,17 @@ pub const SIGPWR: usize = 30; pub const SIGSYS: usize = 31; pub const NSIG: usize = 32; -pub const SIGRTMIN: usize = 34; +pub const SIGRTMIN: usize = 35; pub const SIGRTMAX: usize = 64; -pub const SA_NOCLDSTOP: usize = 0x00000001; -pub const SA_NOCLDWAIT: usize = 0x00000002; -pub const SA_SIGINFO: usize = 0x00000004; -pub const SA_RESTORER: usize = 0x04000000; -pub const SA_ONSTACK: usize = 0x08000000; -pub const SA_RESTART: usize = 0x10000000; -pub const SA_NODEFER: usize = 0x40000000; -pub const SA_RESETHAND: usize = 0x80000000; +pub const SA_NOCLDWAIT: usize = 0x0000_0002; +pub const SA_RESTORER: usize = 0x0000_0004; // TODO: remove +pub const SA_SIGINFO: usize = 0x0200_0000; +pub const SA_ONSTACK: usize = 0x0400_0000; +pub const SA_RESTART: usize = 0x0800_0000; +pub const SA_NODEFER: usize = 0x1000_0000; +pub const SA_RESETHAND: usize = 0x2000_0000; +pub const SA_NOCLDSTOP: usize = 0x4000_0000; pub const SS_ONSTACK: usize = 0x00000001; pub const SS_DISABLE: usize = 0x00000002; diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 0bd7a566a6..135ec59dc0 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -1,7 +1,7 @@ use super::{ debug::{RTLDDebug, _r_debug}, linker::Symbol, - tcb::{round_up, Master}, + tcb::Master, }; use crate::{ header::{errno::STR_ERROR, sys_mman}, @@ -170,7 +170,7 @@ impl DSO { for ph in elf.program_headers.iter() { let voff = ph.p_vaddr % ph.p_align; let vaddr = (ph.p_vaddr - voff) as usize; - let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize); + let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize); match ph.p_type { program_header::PT_DYNAMIC => { @@ -253,7 +253,7 @@ impl DSO { for ph in elf.program_headers.iter() { let voff = ph.p_vaddr % ph.p_align; let vaddr = (ph.p_vaddr - voff) as usize; - let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize); + let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize); match ph.p_type { program_header::PT_LOAD => { diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index 6e76e53b84..983715cbfd 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -27,7 +27,7 @@ use super::{ callbacks::LinkerCallbacks, debug::{RTLDState, _dl_debug_state, _r_debug}, dso::{is_pie_enabled, DSO}, - tcb::{round_up, Master, Tcb}, + tcb::{Master, Tcb}, ExpectTlsFree, PATH_SEP, }; @@ -453,7 +453,7 @@ impl Linker { { let voff = ph.p_vaddr % ph.p_align; let vaddr = (ph.p_vaddr - voff) as usize; - let vsize = round_up((ph.p_memsz + voff) as usize, ph.p_align as usize); + let vsize = ((ph.p_memsz + voff) as usize).next_multiple_of(ph.p_align as usize); let mut prot = 0; if ph.p_flags & program_header::PF_R == program_header::PF_R { prot |= sys_mman::PROT_READ; diff --git a/src/ld_so/mod.rs b/src/ld_so/mod.rs index 809dad7ebd..db559cb886 100644 --- a/src/ld_so/mod.rs +++ b/src/ld_so/mod.rs @@ -22,47 +22,14 @@ pub mod linker; pub mod start; pub mod tcb; +pub use generic_rt::{panic_notls, ExpectTlsFree}; + static mut STATIC_TCB_MASTER: Master = Master { ptr: ptr::null_mut(), len: 0, offset: 0, }; -fn panic_notls(msg: impl core::fmt::Display) -> ! { - eprintln!("panicked in ld.so: {}", msg); - - core::intrinsics::abort(); -} - -pub trait ExpectTlsFree { - type Unwrapped; - - fn expect_notls(self, msg: &str) -> Self::Unwrapped; -} -impl ExpectTlsFree for Result { - type Unwrapped = T; - - fn expect_notls(self, msg: &str) -> T { - match self { - Ok(t) => t, - Err(err) => panic_notls(format_args!( - "{}: expect failed for Result with err: {:?}", - msg, err - )), - } - } -} -impl ExpectTlsFree for Option { - type Unwrapped = T; - - fn expect_notls(self, msg: &str) -> T { - match self { - Some(t) => t, - None => panic_notls(format_args!("{}: expect failed for Option", msg)), - } - } -} - #[inline(never)] pub fn static_init(sp: &'static Stack) { let mut phdr_opt = None; diff --git a/src/ld_so/tcb.rs b/src/ld_so/tcb.rs index fd3ac90bb2..efee9ce9ab 100644 --- a/src/ld_so/tcb.rs +++ b/src/ld_so/tcb.rs @@ -1,5 +1,13 @@ use alloc::vec::Vec; -use core::{arch::asm, cell::UnsafeCell, mem, ptr, slice, sync::atomic::AtomicBool}; +use core::{ + arch::asm, + cell::UnsafeCell, + mem, + ops::{Deref, DerefMut}, + ptr, slice, + sync::atomic::AtomicBool, +}; +use generic_rt::GenericTcb; use goblin::error::{Error, Result}; use super::ExpectTlsFree; @@ -29,18 +37,17 @@ impl Master { } } +#[cfg(target_os = "linux")] +type OsSpecific = (); + +#[cfg(target_os = "redox")] +type OsSpecific = redox_rt::signal::RtSigarea; + #[derive(Debug)] #[repr(C)] // FIXME: Only return &Tcb, and use interior mutability, since it contains the Pthread struct pub struct Tcb { - /// Pointer to the end of static TLS. Must be the first member - pub tls_end: *mut u8, - /// Size of the memory allocated for the static TLS in bytes (multiple of page size) - pub tls_len: usize, - /// Pointer to this structure - pub tcb_ptr: *mut Tcb, - /// Size of the memory allocated for this structure in bytes (should be same as page size) - pub tcb_len: usize, + pub generic: GenericTcb, /// Pointer to a list of initial TLS data pub masters_ptr: *mut Master, /// Size of the masters list in bytes (multiple of mem::size_of::()) @@ -55,21 +62,31 @@ pub struct Tcb { pub pthread: Pthread, } +#[cfg(target_os = "redox")] +const _: () = { + if mem::size_of::() > syscall::PAGE_SIZE { + panic!("too large TCB!"); + } +}; + impl Tcb { /// Create a new TCB pub unsafe fn new(size: usize) -> Result<&'static mut Self> { let page_size = Sys::getpagesize(); - let (abi_page, tls, tcb_page) = Self::os_new(round_up(size, page_size))?; + let (abi_page, tls, tcb_page) = Self::os_new(size.next_multiple_of(page_size))?; let tcb_ptr = tcb_page.as_mut_ptr() as *mut Self; trace!("New TCB: {:p}", tcb_ptr); ptr::write( tcb_ptr, Self { - tls_end: tls.as_mut_ptr().add(tls.len()), - tls_len: tls.len(), - tcb_ptr, - tcb_len: tcb_page.len(), + generic: GenericTcb { + tls_end: tls.as_mut_ptr().add(tls.len()), + tls_len: tls.len(), + tcb_ptr: tcb_ptr.cast(), + tcb_len: tcb_page.len(), + os_specific: OsSpecific::default(), + }, masters_ptr: ptr::null_mut(), masters_len: 0, num_copied_masters: 0, @@ -92,13 +109,7 @@ impl Tcb { /// Get the current TCB pub unsafe fn current() -> Option<&'static mut Self> { - let tcb_ptr = Self::arch_read(offset_of!(Self, tcb_ptr)) as *mut Self; - let tcb_len = Self::arch_read(offset_of!(Self, tcb_len)); - if tcb_ptr.is_null() || tcb_len < mem::size_of::() { - None - } else { - Some(&mut *tcb_ptr) - } + Some(&mut *GenericTcb::::current_ptr()?.cast()) } /// A slice for all of the TLS data @@ -218,50 +229,6 @@ impl Tcb { Ok((abi, tls, tcb)) } - /// Architecture specific code to read a usize from the TCB - aarch64 - #[inline(always)] - #[cfg(target_arch = "aarch64")] - unsafe fn arch_read(offset: usize) -> usize { - let abi_ptr: usize; - asm!( - "mrs {}, tpidr_el0", - out(reg) abi_ptr, - ); - - let tcb_ptr = *(abi_ptr as *const usize); - *((tcb_ptr + offset) as *const usize) - } - - /// Architecture specific code to read a usize from the TCB - x86 - #[inline(always)] - #[cfg(target_arch = "x86")] - unsafe fn arch_read(offset: usize) -> usize { - let value; - asm!( - " - mov {}, gs:[{}] - ", - out(reg) value, - in(reg) offset, - ); - value - } - - /// Architecture specific code to read a usize from the TCB - x86_64 - #[inline(always)] - #[cfg(target_arch = "x86_64")] - unsafe fn arch_read(offset: usize) -> usize { - let value; - asm!( - " - mov {}, fs:[{}] - ", - out(reg) value, - in(reg) offset, - ); - value - } - /// OS and architecture specific code to activate TLS - Linux x86_64 #[cfg(all(target_os = "linux", target_arch = "x86_64"))] unsafe fn os_arch_activate(tls_end: usize, _tls_len: usize) { @@ -269,59 +236,21 @@ impl Tcb { syscall!(ARCH_PRCTL, ARCH_SET_FS, tls_end); } - /// OS and architecture specific code to activate TLS - Redox aarch64 - #[cfg(all(target_os = "redox", target_arch = "aarch64"))] + #[cfg(target_os = "redox")] unsafe fn os_arch_activate(tls_end: usize, tls_len: usize) { - // Uses ABI page - let abi_ptr = tls_end - tls_len - 16; - ptr::write(abi_ptr as *mut usize, tls_end); - asm!( - "msr tpidr_el0, {}", - in(reg) abi_ptr, - ); - } - - /// OS and architecture specific code to activate TLS - Redox x86 - #[cfg(all(target_os = "redox", target_arch = "x86"))] - unsafe fn os_arch_activate(tls_end: usize, _tls_len: usize) { - let mut env = syscall::EnvRegisters::default(); - - let file = syscall::open( - "thisproc:current/regs/env", - syscall::O_CLOEXEC | syscall::O_RDWR, - ) - .expect_notls("failed to open handle for process registers"); - - let _ = syscall::read(file, &mut env).expect_notls("failed to read gsbase"); - - env.gsbase = tls_end as u32; - - let _ = syscall::write(file, &env).expect_notls("failed to write gsbase"); - - let _ = syscall::close(file); - } - - /// OS and architecture specific code to activate TLS - Redox x86_64 - #[cfg(all(target_os = "redox", target_arch = "x86_64"))] - unsafe fn os_arch_activate(tls_end: usize, _tls_len: usize) { - let mut env = syscall::EnvRegisters::default(); - - let file = syscall::open( - "thisproc:current/regs/env", - syscall::O_CLOEXEC | syscall::O_RDWR, - ) - .expect_notls("failed to open handle for process registers"); - - let _ = syscall::read(file, &mut env).expect_notls("failed to read fsbase"); - - env.fsbase = tls_end as u64; - - let _ = syscall::write(file, &env).expect_notls("failed to write fsbase"); - - let _ = syscall::close(file); + redox_rt::tcb_activate(tls_end, tls_len) } } -pub fn round_up(value: usize, alignment: usize) -> usize { - return (value + alignment - 1) & (!(alignment - 1)); +impl Deref for Tcb { + type Target = GenericTcb; + + fn deref(&self) -> &Self::Target { + &self.generic + } +} +impl DerefMut for Tcb { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.generic + } } diff --git a/src/lib.rs b/src/lib.rs index b44e17f9f7..24afb60432 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,6 @@ extern crate alloc; extern crate cbitset; extern crate goblin; extern crate memchr; -#[macro_use] -extern crate memoffset; extern crate posix_regex; extern crate rand; diff --git a/src/macros.rs b/src/macros.rs index 9e94e9e4f2..23f6809d11 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -352,22 +352,3 @@ macro_rules! strto_float_impl { } }}; } - -#[macro_export] -macro_rules! asmfunction( - ($name:ident : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => { - ::core::arch::global_asm!(concat!(" - .p2align 4 - .section .text.", stringify!($name), ", \"ax\", @progbits - .globl ", stringify!($name), " - .type ", stringify!($name), ", @function - ", stringify!($name), ": - ", $($asmstmt, "\n",)* " - .size ", stringify!($name), ", . - ", stringify!($name), " - "), $($decl = $(sym $symname)?$(const $constval)?),*); - - extern "C" { - pub fn $name(); - } - } -); diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index 266c231b98..c92cd2ff01 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -2,12 +2,15 @@ use core::mem; use super::{ super::{types::*, PalSignal}, - e, Sys, + e, e_raw, Sys, }; -use crate::header::{ - signal::{sigaction, siginfo_t, sigset_t, stack_t, NSIG}, - sys_time::itimerval, - time::timespec, +use crate::{ + header::{ + signal::{sigaction, siginfo_t, sigset_t, stack_t, NSIG, SA_RESTORER}, + sys_time::itimerval, + time::timespec, + }, + pthread::Errno, }; impl PalSignal for Sys { @@ -36,34 +39,67 @@ impl PalSignal for Sys { e(syscall!(SETITIMER, which, new, old)) as c_int } - fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> c_int { - e(unsafe { + fn sigaction( + sig: c_int, + act: Option<&sigaction>, + oact: Option<&mut sigaction>, + ) -> Result<(), Errno> { + extern "C" { + fn __restore_rt(); + } + let act = act.map(|act| { + let mut act_clone = act.clone(); + act_clone.sa_flags |= SA_RESTORER as c_ulong; + act_clone.sa_restorer = Some(__restore_rt); + act_clone + }); + e_raw(unsafe { syscall!( RT_SIGACTION, sig, - act.map_or_else(core::ptr::null, |x| x as *const _), + act.as_ref().map_or_else(core::ptr::null, |x| x as *const _), oact.map_or_else(core::ptr::null_mut, |x| x as *mut _), mem::size_of::() ) - }) as c_int + }) + .map(|_| ()) } - unsafe fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int { - e(syscall!(SIGALTSTACK, ss, old_ss)) as c_int + unsafe fn sigaltstack(ss: Option<&stack_t>, old_ss: Option<&mut stack_t>) -> Result<(), Errno> { + e_raw(syscall!( + SIGALTSTACK, + ss.map_or_else(core::ptr::null, |x| x as *const _), + old_ss.map_or_else(core::ptr::null_mut, |x| x as *mut _) + )) + .map(|_| ()) } - unsafe fn sigpending(set: *mut sigset_t) -> c_int { - e(syscall!(RT_SIGPENDING, set, NSIG / 8)) as c_int + fn sigpending(set: &mut sigset_t) -> Result<(), Errno> { + e_raw(unsafe { + syscall!( + RT_SIGPENDING, + set as *mut sigset_t as usize, + mem::size_of::() + ) + }) + .map(|_| ()) } - unsafe fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int { - e(syscall!( - RT_SIGPROCMASK, - how, - set, - oset, - mem::size_of::() - )) as c_int + fn sigprocmask( + how: c_int, + set: Option<&sigset_t>, + oset: Option<&mut sigset_t>, + ) -> Result<(), Errno> { + e_raw(unsafe { + syscall!( + RT_SIGPROCMASK, + how, + set.map_or_else(core::ptr::null, |x| x as *const _), + oset.map_or_else(core::ptr::null_mut, |x| x as *mut _), + mem::size_of::() + ) + }) + .map(|_| ()) } unsafe fn sigsuspend(set: *const sigset_t) -> c_int { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 9a5fbaa01e..9b598d68a0 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -33,7 +33,7 @@ pub mod rlb; pub mod auxv_defs; #[cfg(target_os = "redox")] -pub use redox_exec::auxv_defs; +pub use redox_rt::auxv_defs; use self::types::*; pub mod types; @@ -306,7 +306,7 @@ pub fn init(auxvs: Box<[[usize; 2]]>) { if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGPROCMASK_HI) { inherited_sigprocmask |= (mask as u64) << 32; } - syscall::sigprocmask(syscall::SIG_SETMASK, Some(&inherited_sigprocmask), None).unwrap(); + redox_rt::signal::set_sigmask(Some(inherited_sigprocmask), None).unwrap(); } #[cfg(not(target_os = "redox"))] pub fn init(auxvs: Box<[[usize; 2]]>) {} diff --git a/src/platform/pal/signal.rs b/src/platform/pal/signal.rs index c085c678e8..a802270fa0 100644 --- a/src/platform/pal/signal.rs +++ b/src/platform/pal/signal.rs @@ -1,8 +1,11 @@ use super::super::{types::*, Pal}; -use crate::header::{ - signal::{sigaction, siginfo_t, sigset_t, stack_t}, - sys_time::itimerval, - time::timespec, +use crate::{ + header::{ + signal::{sigaction, siginfo_t, sigset_t, stack_t}, + sys_time::itimerval, + time::timespec, + }, + pthread::Errno, }; pub trait PalSignal: Pal { @@ -16,13 +19,21 @@ pub trait PalSignal: Pal { unsafe fn setitimer(which: c_int, new: *const itimerval, old: *mut itimerval) -> c_int; - fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> c_int; + fn sigaction( + sig: c_int, + act: Option<&sigaction>, + oact: Option<&mut sigaction>, + ) -> Result<(), Errno>; - unsafe fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int; + unsafe fn sigaltstack(ss: Option<&stack_t>, old_ss: Option<&mut stack_t>) -> Result<(), Errno>; - unsafe fn sigpending(set: *mut sigset_t) -> c_int; + fn sigpending(set: &mut sigset_t) -> Result<(), Errno>; - unsafe fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int; + fn sigprocmask( + how: c_int, + set: Option<&sigset_t>, + oset: Option<&mut sigset_t>, + ) -> Result<(), Errno>; unsafe fn sigsuspend(set: *const sigset_t) -> c_int; diff --git a/src/platform/redox/clone.rs b/src/platform/redox/clone.rs index 336c0937cf..017696ce6b 100644 --- a/src/platform/redox/clone.rs +++ b/src/platform/redox/clone.rs @@ -9,12 +9,9 @@ use syscall::{ use crate::sync::rwlock::Rwlock; -use super::{ - extra::{create_set_addr_space_buf, FdGuard}, - signal::sighandler_function, -}; +use redox_rt::{proc::FdGuard, signal::sighandler_function}; -pub use redox_exec::*; +pub use redox_rt::proc::*; static CLONE_LOCK: Rwlock = Rwlock::new(crate::pthread::Pshared::Private); @@ -35,153 +32,4 @@ pub fn wrlock() -> impl Drop { Guard } - -/// Spawns a new context sharing the same address space as the current one (i.e. a new thread). -pub unsafe fn rlct_clone_impl(stack: *mut usize) -> Result { - let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?); - let (new_pid_fd, new_pid) = new_context()?; - - copy_str(*cur_pid_fd, *new_pid_fd, "name")?; - - // Inherit existing address space - { - let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?); - let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?); - - let buf = create_set_addr_space_buf( - *cur_addr_space_fd, - __relibc_internal_rlct_clone_ret as usize, - stack as usize, - ); - let _ = syscall::write(*new_addr_space_sel_fd, &buf)?; - } - - // Inherit file table - { - let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?); - let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?); - - let _ = syscall::write( - *new_filetable_sel_fd, - &usize::to_ne_bytes(*cur_filetable_fd), - )?; - } - - // Inherit sigactions (on Linux, CLONE_THREAD requires CLONE_SIGHAND which implies the sigactions - // table is reused). - { - let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?); - let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?); - - let _ = syscall::write( - *new_sigaction_sel_fd, - &usize::to_ne_bytes(*cur_sigaction_fd), - )?; - } - // Inherit sighandler, but not the sigaltstack. - { - let new_sighandler_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sighandler")?); - let data = SetSighandlerData { - entry: sighandler_function(), - altstack_base: 0, - altstack_len: 0, - }; - let _ = syscall::write(*new_sighandler_fd, &data)?; - } - - // Sigprocmask starts as "block all", and is initialized when the thread has actually returned - // from clone_ret. - - // TODO: Should some of these registers be inherited? - //copy_env_regs(*cur_pid_fd, *new_pid_fd)?; - - // Unblock context. - let start_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"start")?); - let _ = syscall::write(*start_fd, &[0])?; - - Ok(new_pid) -} - -extern "C" { - fn __relibc_internal_rlct_clone_ret(); -} - -#[cfg(target_arch = "aarch64")] -core::arch::global_asm!( - " - .globl __relibc_internal_rlct_clone_ret - .type __relibc_internal_rlct_clone_ret, @function - .p2align 6 -__relibc_internal_rlct_clone_ret: - # Load registers - ldp x8, x0, [sp], #16 - ldp x1, x2, [sp], #16 - ldp x3, x4, [sp], #16 - - # Call entry point - blr x8 - - ret - .size __relibc_internal_rlct_clone_ret, . - __relibc_internal_rlct_clone_ret -" -); - -#[cfg(target_arch = "x86")] -core::arch::global_asm!( - " - .globl __relibc_internal_rlct_clone_ret - .type __relibc_internal_rlct_clone_ret, @function - .p2align 6 -__relibc_internal_rlct_clone_ret: - # Load registers - pop eax - - sub esp, 8 - - mov DWORD PTR [esp], 0x00001F80 - # TODO: ldmxcsr [esp] - mov WORD PTR [esp], 0x037F - fldcw [esp] - - add esp, 8 - - # Call entry point - call eax - - ret - .size __relibc_internal_rlct_clone_ret, . - __relibc_internal_rlct_clone_ret -" -); - -#[cfg(target_arch = "x86_64")] -core::arch::global_asm!( - " - .globl __relibc_internal_rlct_clone_ret - .type __relibc_internal_rlct_clone_ret, @function - .p2align 6 -__relibc_internal_rlct_clone_ret: - # Load registers - pop rax - pop rdi - pop rsi - pop rdx - pop rcx - pop r8 - pop r9 - - sub rsp, 8 - - mov DWORD PTR [rsp], 0x00001F80 - ldmxcsr [rsp] - mov WORD PTR [rsp], 0x037F - fldcw [rsp] - - add rsp, 8 - - # Call entry point - call rax - - ret - .size __relibc_internal_rlct_clone_ret, . - __relibc_internal_rlct_clone_ret -" -); +pub use redox_rt::thread::*; diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index 4323242cad..5dda2e77c1 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -9,7 +9,7 @@ use crate::{ }, }; -use redox_exec::{ExtraInfo, FdGuard, FexecResult}; +use redox_rt::proc::{ExtraInfo, FdGuard, FexecResult, InterpOverride}; use syscall::{data::Stat, error::*, flag::*}; fn fexec_impl( @@ -20,11 +20,11 @@ fn fexec_impl( envs: &[&[u8]], total_args_envs_size: usize, extrainfo: &ExtraInfo, - interp_override: Option, + interp_override: Option, ) -> Result { let memory = FdGuard::new(syscall::open("memory:", 0)?); - let addrspace_selection_fd = match redox_exec::fexec_impl( + let addrspace_selection_fd = match redox_rt::proc::fexec_impl( exec_file, open_via_dup, &memory, @@ -88,7 +88,7 @@ pub enum Executable<'a> { pub fn execve( exec: Executable<'_>, arg_env: ArgEnv, - interp_override: Option, + interp_override: Option, ) -> Result { // NOTE: We must omit O_CLOEXEC and close manually, otherwise it will be closed before we // have even read it! @@ -305,8 +305,7 @@ pub fn execve( unreachable!() } else { - let mut sigprocmask = 0_u64; - syscall::sigprocmask(syscall::SIG_SETMASK, None, Some(&mut sigprocmask)).unwrap(); + let sigprocmask = redox_rt::signal::get_sigmask().unwrap(); let extrainfo = ExtraInfo { cwd: Some(&cwd), diff --git a/src/platform/redox/extra.rs b/src/platform/redox/extra.rs index 5dd09c274c..3c96933cdd 100644 --- a/src/platform/redox/extra.rs +++ b/src/platform/redox/extra.rs @@ -3,7 +3,7 @@ use core::{ptr, slice}; use crate::platform::{sys::e, types::*}; use syscall::{error::*, F_SETFD, F_SETFL}; -pub use redox_exec::*; +pub use redox_rt::proc::FdGuard; #[no_mangle] pub unsafe extern "C" fn redox_fpath(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t { diff --git a/src/platform/redox/libredox.rs b/src/platform/redox/libredox.rs index 15ea59a7ed..cdcf546110 100644 --- a/src/platform/redox/libredox.rs +++ b/src/platform/redox/libredox.rs @@ -1,14 +1,21 @@ use core::{slice, str}; +use redox_rt::sys::{posix_read, posix_write}; use syscall::{Error, Result, WaitFlags, EMFILE}; use crate::{ header::{ - errno::EINVAL, signal::sigaction, sys_stat::UTIME_NOW, sys_uio::iovec, time::timespec, + errno::EINVAL, + signal::{sigaction, SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK}, + sys_stat::UTIME_NOW, + sys_uio::iovec, + time::timespec, }, - platform::types::*, + platform::{types::*, PalSignal}, }; +use super::Sys; + pub type RawResult = usize; pub fn open(path: &str, oflag: c_int, mode: mode_t) -> Result { @@ -141,10 +148,7 @@ pub unsafe extern "C" fn redox_dup2_v1( } #[no_mangle] pub unsafe extern "C" fn redox_read_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult { - Error::mux(syscall::read( - fd, - slice::from_raw_parts_mut(dst_base, dst_len), - )) + Error::mux(posix_read(fd, slice::from_raw_parts_mut(dst_base, dst_len))) } #[no_mangle] pub unsafe extern "C" fn redox_write_v1( @@ -152,7 +156,7 @@ pub unsafe extern "C" fn redox_write_v1( src_base: *const u8, src_len: usize, ) -> RawResult { - Error::mux(syscall::write(fd, slice::from_raw_parts(src_base, src_len))) + Error::mux(posix_write(fd, slice::from_raw_parts(src_base, src_len))) } #[no_mangle] pub unsafe extern "C" fn redox_fsync_v1(fd: usize) -> RawResult { @@ -249,7 +253,11 @@ pub unsafe extern "C" fn redox_sigaction_v1( new: *const sigaction, old: *mut sigaction, ) -> RawResult { - Error::mux(super::signal::sigaction_impl(signal as i32, new.as_ref(), old.as_mut()).map(|()| 0)) + Error::mux( + Sys::sigaction(signal as c_int, new.as_ref(), old.as_mut()) + .map(|()| 0) + .map_err(Into::into), + ) } #[no_mangle] @@ -258,7 +266,11 @@ pub unsafe extern "C" fn redox_sigprocmask_v1( new: *const u64, old: *mut u64, ) -> RawResult { - Error::mux(super::signal::sigprocmask_impl(how as i32, new, old).map(|()| 0)) + Error::mux( + Sys::sigprocmask(how as c_int, new.as_ref(), old.as_mut()) + .map(|()| 0) + .map_err(Into::into), + ) } #[no_mangle] pub unsafe extern "C" fn redox_mmap_v1( diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index 079031c28b..c1d3b645f3 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -10,7 +10,7 @@ use crate::{ fs::File, header::{ dirent::dirent, - errno::{EBADR, EINVAL, EIO, ENOENT, ENOMEM, ENOSYS, EPERM, ERANGE}, + errno::{EBADF, EBADFD, EBADR, EINVAL, EIO, ENOENT, ENOMEM, ENOSYS, EPERM, ERANGE}, fcntl, limits, sys_mman::{MAP_ANONYMOUS, MAP_FAILED, PROT_READ, PROT_WRITE}, sys_random, @@ -27,7 +27,7 @@ use crate::{ pthread::{self, Errno, ResultExt}, }; -pub use redox_exec::FdGuard; +pub use redox_rt::proc::FdGuard; use super::{types::*, Pal, Read, ERRNO}; @@ -217,7 +217,7 @@ impl Pal for Sys { } fn exit(status: c_int) -> ! { - let _ = syscall::exit(status as usize); + let _ = syscall::exit((status as usize) << 8); loop {} } @@ -824,7 +824,8 @@ impl Pal for Sys { } fn read(fd: c_int, buf: &mut [u8]) -> Result { - Ok(syscall::read(fd as usize, buf)? as ssize_t) + let fd = usize::try_from(fd).map_err(|_| Errno(EBADF))?; + Ok(redox_rt::sys::posix_read(fd, buf)? as ssize_t) } fn pread(fd: c_int, buf: &mut [u8], offset: off_t) -> Result { unsafe { @@ -1124,7 +1125,8 @@ impl Pal for Sys { } fn write(fd: c_int, buf: &[u8]) -> Result { - Ok(syscall::write(fd as usize, buf)? as ssize_t) + let fd = usize::try_from(fd).map_err(|_| Errno(EBADFD))?; + Ok(redox_rt::sys::posix_write(fd, buf)? as ssize_t) } fn pwrite(fd: c_int, buf: &[u8], offset: off_t) -> Result { unsafe { diff --git a/src/platform/redox/path.rs b/src/platform/redox/path.rs index bdcb4a4254..01c075748b 100644 --- a/src/platform/redox/path.rs +++ b/src/platform/redox/path.rs @@ -1,4 +1,5 @@ use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec}; +use redox_rt::signal::tmp_disable_signals; use syscall::{data::Stat, error::*, flag::*}; use super::{libcscheme, FdGuard}; @@ -19,7 +20,7 @@ const PATH_MAX: usize = 4096; // and after acquiring the locks. (TODO: ArcSwap? That will need to be ported to no_std first, // though). pub fn chdir(path: &str) -> Result<()> { - let _siglock = SignalMask::lock(); + let _siglock = tmp_disable_signals(); let mut cwd_guard = CWD.lock(); let canonicalized = @@ -40,13 +41,13 @@ pub fn chdir(path: &str) -> Result<()> { } pub fn clone_cwd() -> Option> { - let _siglock = SignalMask::lock(); + let _siglock = tmp_disable_signals(); CWD.lock().clone() } // TODO: MaybeUninit pub fn getcwd(buf: &mut [u8]) -> Option { - let _siglock = SignalMask::lock(); + let _siglock = tmp_disable_signals(); let cwd_guard = CWD.lock(); let cwd = cwd_guard.as_deref().unwrap_or("").as_bytes(); @@ -61,9 +62,8 @@ pub fn getcwd(buf: &mut [u8]) -> Option { Some(cwd.len()) } -// TODO: Move cwd from kernel to libc. It is passed via auxiliary vectors. pub fn canonicalize(path: &str) -> Result { - let _siglock = SignalMask::lock(); + let _siglock = tmp_disable_signals(); let cwd = CWD.lock(); canonicalize_using_cwd(cwd.as_deref(), path).ok_or(Error::new(ENOENT)) } @@ -72,29 +72,10 @@ pub fn canonicalize(path: &str) -> Result { static CWD: Mutex>> = Mutex::new(None); pub fn setcwd_manual(cwd: Box) { - let _siglock = SignalMask::lock(); + let _siglock = tmp_disable_signals(); *CWD.lock() = Some(cwd); } -/// RAII guard able to magically fix signal unsafety, by disabling signals during a critical -/// section. -pub struct SignalMask { - oldset: u64, -} -impl SignalMask { - pub fn lock() -> Self { - let mut oldset = 0; - syscall::sigprocmask(syscall::SIG_SETMASK, Some(&!0), Some(&mut oldset)) - .expect("failed to run sigprocmask"); - Self { oldset } - } -} -impl Drop for SignalMask { - fn drop(&mut self) { - let _ = syscall::sigprocmask(syscall::SIG_SETMASK, Some(&self.oldset), None); - } -} - pub fn open(path: &str, flags: usize) -> Result { // TODO: SYMLOOP_MAX const MAX_LEVEL: usize = 64; diff --git a/src/platform/redox/redox-exec/src/arch/aarch64.rs b/src/platform/redox/redox-exec/src/arch/aarch64.rs deleted file mode 100644 index 058ba0280f..0000000000 --- a/src/platform/redox/redox-exec/src/arch/aarch64.rs +++ /dev/null @@ -1,99 +0,0 @@ -use syscall::error::*; - -use crate::{fork_inner, FdGuard}; - -// Setup a stack starting from the very end of the address space, and then growing downwards. -pub(crate) const STACK_TOP: usize = 1 << 47; -pub(crate) const STACK_SIZE: usize = 1024 * 1024; - -/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS -/// is already initialized as if it was a thread. -pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> { - let mut env = syscall::EnvRegisters::default(); - - let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?); - - env.tpidr_el0 = 0; - - let _ = syscall::write(*file, &mut env)?; - Ok(()) -} - -pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { - // Copy environment registers. - { - let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); - let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); - - let mut env_regs = syscall::EnvRegisters::default(); - let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; - let _ = syscall::write(*new_env_regs_fd, &env_regs)?; - } - - Ok(()) -} - -#[no_mangle] -unsafe extern "C" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) -} - -#[no_mangle] -unsafe extern "C" fn __relibc_internal_fork_hook(cur_filetable_fd: usize, new_pid_fd: usize) { - let _ = syscall::close(cur_filetable_fd); - let _ = syscall::close(new_pid_fd); -} - -core::arch::global_asm!( - " - .p2align 6 - .globl __relibc_internal_fork_wrapper - .type __relibc_internal_fork_wrapper, @function -__relibc_internal_fork_wrapper: - stp x29, x30, [sp, #-16]! - stp x27, x28, [sp, #-16]! - stp x25, x26, [sp, #-16]! - stp x23, x24, [sp, #-16]! - stp x21, x22, [sp, #-16]! - stp x19, x20, [sp, #-16]! - - sub sp, sp, #32 - - //TODO: store floating point regs - - mov x0, sp - bl __relibc_internal_fork_impl - b 2f - - .size __relibc_internal_fork_wrapper, . - __relibc_internal_fork_wrapper - - .p2align 6 - .globl __relibc_internal_fork_ret - .type __relibc_internal_fork_ret, @function -__relibc_internal_fork_ret: - ldp x0, x1, [sp] - bl __relibc_internal_fork_hook - - //TODO: load floating point regs - - mov x0, xzr - - .p2align 4 -2: - add sp, sp, #32 - ldp x19, x20, [sp], #16 - ldp x21, x22, [sp], #16 - ldp x23, x24, [sp], #16 - ldp x25, x26, [sp], #16 - ldp x27, x28, [sp], #16 - ldp x29, x30, [sp], #16 - - ret - - .size __relibc_internal_fork_ret, . - __relibc_internal_fork_ret" -); - -extern "C" { - pub(crate) fn __relibc_internal_fork_wrapper() -> usize; - pub(crate) fn __relibc_internal_fork_ret(); -} diff --git a/src/platform/redox/redox-exec/src/arch/x86.rs b/src/platform/redox/redox-exec/src/arch/x86.rs deleted file mode 100644 index c6addedb38..0000000000 --- a/src/platform/redox/redox-exec/src/arch/x86.rs +++ /dev/null @@ -1,107 +0,0 @@ -use syscall::error::*; - -use crate::{fork_inner, FdGuard}; - -// Setup a stack starting from the very end of the address space, and then growing downwards. -pub(crate) const STACK_TOP: usize = 1 << 31; -pub(crate) const STACK_SIZE: usize = 1024 * 1024; - -/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS -/// is already initialized as if it was a thread. -pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> { - let mut env = syscall::EnvRegisters::default(); - - let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?); - - env.fsbase = 0; - env.gsbase = 0; - - let _ = syscall::write(*file, &mut env)?; - Ok(()) -} - -pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { - // Copy environment registers. - { - let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); - let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); - - let mut env_regs = syscall::EnvRegisters::default(); - let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; - let _ = syscall::write(*new_env_regs_fd, &env_regs)?; - } - - Ok(()) -} - -#[no_mangle] -unsafe extern "cdecl" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) -} - -#[no_mangle] -unsafe extern "cdecl" fn __relibc_internal_fork_hook(cur_filetable_fd: usize, new_pid_fd: usize) { - let _ = syscall::close(cur_filetable_fd); - let _ = syscall::close(new_pid_fd); -} - -//TODO: x86 -core::arch::global_asm!( - " - .p2align 6 - .globl __relibc_internal_fork_wrapper - .type __relibc_internal_fork_wrapper, @function -__relibc_internal_fork_wrapper: - push ebp - mov ebp, esp - - // Push preserved registers - push ebx - push edi - push esi - push ebp - - sub esp, 32 - - //TODO stmxcsr [esp+16] - fnstcw [esp+24] - - push esp - call __relibc_internal_fork_impl - pop esp - jmp 2f - - .size __relibc_internal_fork_wrapper, . - __relibc_internal_fork_wrapper - - .p2align 6 - .globl __relibc_internal_fork_ret - .type __relibc_internal_fork_ret, @function -__relibc_internal_fork_ret: - // Arguments already on the stack - call __relibc_internal_fork_hook - - //TODO ldmxcsr [esp+16] - fldcw [esp+24] - - xor eax, eax - - .p2align 4 -2: - add esp, 32 - - // Pop preserved registers - pop ebp - pop esi - pop edi - pop ebx - - pop ebp - ret - - .size __relibc_internal_fork_ret, . - __relibc_internal_fork_ret" -); - -extern "cdecl" { - pub(crate) fn __relibc_internal_fork_wrapper() -> usize; - pub(crate) fn __relibc_internal_fork_ret(); -} diff --git a/src/platform/redox/redox-exec/src/arch/x86_64.rs b/src/platform/redox/redox-exec/src/arch/x86_64.rs deleted file mode 100644 index 17aef45681..0000000000 --- a/src/platform/redox/redox-exec/src/arch/x86_64.rs +++ /dev/null @@ -1,107 +0,0 @@ -use syscall::error::*; - -use crate::{fork_inner, FdGuard}; - -// Setup a stack starting from the very end of the address space, and then growing downwards. -pub(crate) const STACK_TOP: usize = 1 << 47; -pub(crate) const STACK_SIZE: usize = 1024 * 1024; - -/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS -/// is already initialized as if it was a thread. -pub unsafe fn deactivate_tcb(open_via_dup: usize) -> Result<()> { - let mut env = syscall::EnvRegisters::default(); - - let file = FdGuard::new(syscall::dup(open_via_dup, b"regs/env")?); - - env.fsbase = 0; - env.gsbase = 0; - - let _ = syscall::write(*file, &mut env)?; - Ok(()) -} - -pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> { - // Copy environment registers. - { - let cur_env_regs_fd = FdGuard::new(syscall::dup(cur_pid_fd, b"regs/env")?); - let new_env_regs_fd = FdGuard::new(syscall::dup(new_pid_fd, b"regs/env")?); - - let mut env_regs = syscall::EnvRegisters::default(); - let _ = syscall::read(*cur_env_regs_fd, &mut env_regs)?; - let _ = syscall::write(*new_env_regs_fd, &env_regs)?; - } - - Ok(()) -} - -#[no_mangle] -unsafe extern "sysv64" fn __relibc_internal_fork_impl(initial_rsp: *mut usize) -> usize { - Error::mux(fork_inner(initial_rsp)) -} - -#[no_mangle] -unsafe extern "sysv64" fn __relibc_internal_fork_hook(cur_filetable_fd: usize, new_pid_fd: usize) { - let _ = syscall::close(cur_filetable_fd); - let _ = syscall::close(new_pid_fd); -} - -core::arch::global_asm!( - " - .p2align 6 - .globl __relibc_internal_fork_wrapper - .type __relibc_internal_fork_wrapper, @function -__relibc_internal_fork_wrapper: - push rbp - mov rbp, rsp - - push rbx - push rbp - push r12 - push r13 - push r14 - push r15 - - sub rsp, 32 - - stmxcsr [rsp+16] - fnstcw [rsp+24] - - mov rdi, rsp - call __relibc_internal_fork_impl - jmp 2f - - .size __relibc_internal_fork_wrapper, . - __relibc_internal_fork_wrapper - - .p2align 6 - .globl __relibc_internal_fork_ret - .type __relibc_internal_fork_ret, @function -__relibc_internal_fork_ret: - mov rdi, [rsp] - mov rsi, [rsp + 8] - call __relibc_internal_fork_hook - - ldmxcsr [rsp+16] - fldcw [rsp+24] - - xor rax, rax - - .p2align 4 -2: - add rsp, 32 - pop r15 - pop r14 - pop r13 - pop r12 - pop rbp - pop rbx - - pop rbp - ret - - .size __relibc_internal_fork_ret, . - __relibc_internal_fork_ret" -); - -extern "sysv64" { - pub(crate) fn __relibc_internal_fork_wrapper() -> usize; - pub(crate) fn __relibc_internal_fork_ret(); -} diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 25254ff262..ce156d1dea 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -1,5 +1,6 @@ use core::mem; -use syscall::{self, number::SYS_SIGRETURN, Result, SetSighandlerData, SignalStack}; +use redox_rt::signal::{Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler}; +use syscall::{self, Result}; use super::{ super::{types::*, Pal, PalSignal}, @@ -8,11 +9,15 @@ use super::{ use crate::{ header::{ errno::{EINVAL, ENOSYS}, - signal::{sigaction, siginfo_t, sigset_t, stack_t}, + signal::{ + sigaction, siginfo_t, sigset_t, stack_t, SA_SIGINFO, SIG_BLOCK, SIG_DFL, SIG_IGN, + SIG_SETMASK, SIG_UNBLOCK, SS_DISABLE, SS_ONSTACK, + }, sys_time::{itimerval, ITIMER_REAL}, time::timespec, }, platform::ERRNO, + pthread::Errno, }; impl PalSignal for Sys { @@ -48,11 +53,11 @@ impl PalSignal for Sys { } fn kill(pid: pid_t, sig: c_int) -> c_int { - e(syscall::kill(pid as usize, sig as usize)) as c_int + e(redox_rt::sys::posix_kill(pid as usize, sig as usize).map(|()| 0)) as c_int } fn killpg(pgrp: pid_t, sig: c_int) -> c_int { - e(syscall::kill(-(pgrp as isize) as usize, sig as usize)) as c_int + e(redox_rt::sys::posix_killpg(pgrp as usize, sig as usize).map(|()| 0)) as c_int } fn raise(sig: c_int) -> c_int { @@ -104,21 +109,137 @@ impl PalSignal for Sys { 0 } - fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> c_int { - e(sigaction_impl(sig, act, oact).map(|()| 0)) as c_int + fn sigaction( + sig: c_int, + c_act: Option<&sigaction>, + c_oact: Option<&mut sigaction>, + ) -> Result<(), Errno> { + let sig = u8::try_from(sig).map_err(|_| syscall::Error::new(syscall::EINVAL))?; + + let new_action = c_act.map(|c_act| { + let handler = c_act.sa_handler.map_or(0, |f| f as usize); + + let kind = if handler == SIG_DFL { + SigactionKind::Default + } else if handler == SIG_IGN { + SigactionKind::Ignore + } else { + SigactionKind::Handled { + handler: if c_act.sa_flags & crate::header::signal::SA_SIGINFO as c_ulong != 0 { + SignalHandler { + sigaction: unsafe { core::mem::transmute(c_act.sa_handler) }, + } + } else { + SignalHandler { + handler: c_act.sa_handler, + } + }, + } + }; + + Sigaction { + kind, + mask: c_act.sa_mask, + flags: SigactionFlags::from_bits_retain(c_act.sa_flags as u32), + } + }); + let mut old_action = c_oact.as_ref().map(|_| Sigaction::default()); + + redox_rt::signal::sigaction(sig, new_action.as_ref(), old_action.as_mut())?; + + if let (Some(c_oact), Some(old_action)) = (c_oact, old_action) { + *c_oact = match old_action.kind { + SigactionKind::Ignore => sigaction { + sa_handler: unsafe { core::mem::transmute(SIG_IGN) }, + sa_flags: 0, + sa_restorer: None, + sa_mask: 0, + }, + SigactionKind::Default => sigaction { + sa_handler: unsafe { core::mem::transmute(SIG_DFL) }, + sa_flags: 0, + sa_restorer: None, + sa_mask: 0, + }, + SigactionKind::Handled { handler } => sigaction { + sa_handler: if old_action.flags.contains(SigactionFlags::SIGINFO) { + unsafe { core::mem::transmute(handler.sigaction) } + } else { + unsafe { handler.handler } + }, + sa_restorer: None, + sa_flags: old_action.flags.bits().into(), + sa_mask: old_action.mask, + }, + }; + } + Ok(()) } - unsafe fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int { - unimplemented!() + unsafe fn sigaltstack( + new_c: Option<&stack_t>, + old_c: Option<&mut stack_t>, + ) -> Result<(), Errno> { + let new = new_c + .map(|c_stack| { + let flags = usize::try_from(c_stack.ss_flags).map_err(|_| Errno(EINVAL))?; + if flags != flags & (SS_DISABLE | SS_ONSTACK) { + return Err(Errno(EINVAL)); + } + + Ok(if flags & SS_DISABLE == SS_DISABLE { + Sigaltstack::Disabled + } else { + Sigaltstack::Enabled { + onstack: false, + base: c_stack.ss_sp.cast(), + size: c_stack.ss_size, + } + }) + }) + .transpose()?; + + let mut old = old_c.as_ref().map(|_| Sigaltstack::default()); + redox_rt::signal::sigaltstack(new.as_ref(), old.as_mut())?; + + if let (Some(old_c_stack), Some(old)) = (old_c, old) { + *old_c_stack = match old { + Sigaltstack::Disabled => stack_t { + ss_sp: core::ptr::null_mut(), + ss_size: 0, + ss_flags: SS_DISABLE.try_into().unwrap(), + }, + Sigaltstack::Enabled { + onstack, + base, + size, + } => stack_t { + ss_sp: base.cast(), + ss_size: size, + ss_flags: SS_ONSTACK.try_into().unwrap(), + }, + }; + } + Ok(()) } - unsafe fn sigpending(set: *mut sigset_t) -> c_int { - ERRNO.set(ENOSYS); - -1 + fn sigpending(set: &mut sigset_t) -> Result<(), Errno> { + *set = redox_rt::signal::currently_pending(); + Ok(()) } - unsafe fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int { - e(sigprocmask_impl(how, set, oset).map(|()| 0)) as c_int + fn sigprocmask( + how: c_int, + set: Option<&sigset_t>, + oset: Option<&mut sigset_t>, + ) -> Result<(), Errno> { + Ok(match how { + SIG_SETMASK => redox_rt::signal::set_sigmask(set.copied(), oset)?, + SIG_BLOCK => redox_rt::signal::or_sigmask(set.copied(), oset)?, + SIG_UNBLOCK => redox_rt::signal::andn_sigmask(set.copied(), oset)?, + + _ => return Err(Errno(EINVAL)), + }) } unsafe fn sigsuspend(set: *const sigset_t) -> c_int { @@ -135,173 +256,3 @@ impl PalSignal for Sys { -1 } } - -pub(crate) fn sigaction_impl( - sig: i32, - act: Option<&sigaction>, - oact: Option<&mut sigaction>, -) -> Result<()> { - let new_opt = act.map(|act| { - let sa_handler = unsafe { mem::transmute(act.sa_handler) }; - syscall::SigAction { - sa_handler, - sa_mask: act.sa_mask as u64, - sa_flags: syscall::SigActionFlags::from_bits(act.sa_flags as usize) - .expect("sigaction: invalid bit pattern"), - } - }); - let mut old_opt = oact.as_ref().map(|_| syscall::SigAction::default()); - syscall::sigaction(sig as usize, new_opt.as_ref(), old_opt.as_mut())?; - if let (Some(old), Some(oact)) = (old_opt, oact) { - oact.sa_handler = unsafe { mem::transmute(old.sa_handler) }; - oact.sa_mask = old.sa_mask as sigset_t; - oact.sa_flags = old.sa_flags.bits() as c_ulong; - } - Ok(()) -} -pub(crate) unsafe fn sigprocmask_impl( - how: i32, - set: *const sigset_t, - oset: *mut sigset_t, -) -> Result<()> { - syscall::sigprocmask(how as usize, set.as_ref(), oset.as_mut())?; - Ok(()) -} -#[cfg(target_arch = "x86_64")] -static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); - -pub fn sighandler_function() -> usize { - #[cfg(target_arch = "x86_64")] - // Check OSXSAVE bit - // TODO: HWCAP? - if CPUID_EAX1_ECX.load(core::sync::atomic::Ordering::Relaxed) & (1 << 27) != 0 { - __relibc_internal_sigentry_xsave as usize - } else { - __relibc_internal_sigentry_fxsave as usize - } - - #[cfg(any(target_arch = "x86", target_arch = "aarch64"))] - { - __relibc_internal_sigentry as usize - } -} - -pub fn setup_sighandler() { - use core::mem::size_of; - - // TODO - let altstack_base = 0_usize; - let altstack_len = 0_usize; - - #[cfg(target_arch = "x86_64")] - { - let cpuid_eax1_ecx = unsafe { core::arch::x86_64::__cpuid(1) }.ecx; - CPUID_EAX1_ECX.store(cpuid_eax1_ecx, core::sync::atomic::Ordering::Relaxed); - } - - let data = SetSighandlerData { - entry: sighandler_function(), - altstack_base, - altstack_len, - }; - - let fd = syscall::open( - "thisproc:current/sighandler", - syscall::O_WRONLY | syscall::O_CLOEXEC, - ) - .expect("failed to open thisproc:current/sighandler"); - syscall::write(fd, &data).expect("failed to write to thisproc:current/sighandler"); - let _ = syscall::close(fd); -} - -#[repr(C)] -pub struct SigStack { - #[cfg(target_arch = "x86_64")] - fx: [u8; 4096], - - #[cfg(target_arch = "x86")] - fx: [u8; 512], - - kernel_pushed: SignalStack, -} - -#[inline(always)] -unsafe fn inner(stack: &mut SigStack) { - let handler: extern "C" fn(c_int) = core::mem::transmute(stack.kernel_pushed.sa_handler); - handler(stack.kernel_pushed.sig_num as c_int) -} -#[cfg(not(target_arch = "x86"))] -unsafe extern "C" fn inner_c(stack: usize) { - inner(&mut *(stack as *mut SigStack)) -} -#[cfg(target_arch = "x86")] -unsafe extern "fastcall" fn inner_fastcall(stack: usize) { - inner(&mut *(stack as *mut SigStack)) -} - -// TODO: is the memset necessary? -#[cfg(target_arch = "x86_64")] -asmfunction!(__relibc_internal_sigentry_xsave: [" - sub rsp, 4096 - - cld - mov rdi, rsp - xor eax, eax - mov ecx, 4096 - rep stosb - - mov eax, 0xffffffff - mov edx, eax - xsave [rsp] - - mov rdi, rsp - call {inner} - - mov eax, 0xffffffff - mov edx, eax - xrstor [rsp] - add rsp, 4096 - - mov eax, {SYS_SIGRETURN} - syscall -"] <= [inner = sym inner_c, SYS_SIGRETURN = const SYS_SIGRETURN]); - -#[cfg(target_arch = "x86_64")] -asmfunction!(__relibc_internal_sigentry_fxsave: [" - sub rsp, 4096 - - fxsave64 [rsp] - - mov rdi, rsp - call {inner} - - fxrstor64 [rsp] - add rsp, 4096 - - mov eax, {SYS_SIGRETURN} - syscall -"] <= [inner = sym inner_c, SYS_SIGRETURN = const SYS_SIGRETURN]); - -#[cfg(target_arch = "x86")] -asmfunction!(__relibc_internal_sigentry: [" - sub esp, 512 - fxsave [esp] - - mov ecx, esp - call {inner} - - add esp, 512 - fxrstor [esp] - - mov eax, {SYS_SIGRETURN} - int 0x80 -"] <= [inner = sym inner_fastcall, SYS_SIGRETURN = const SYS_SIGRETURN]); - -#[cfg(target_arch = "aarch64")] -asmfunction!(__relibc_internal_sigentry: [" - mov x0, sp - bl {inner} - - mov x8, {SYS_SIGRETURN} - svc 0 -"] <= [inner = sym inner_c, SYS_SIGRETURN = const SYS_SIGRETURN]); diff --git a/src/pthread/mod.rs b/src/pthread/mod.rs index 37402d4513..cfc56dd485 100644 --- a/src/pthread/mod.rs +++ b/src/pthread/mod.rs @@ -92,6 +92,12 @@ impl From for Errno { Errno(value.errno) } } +#[cfg(target_os = "redox")] +impl From for syscall::Error { + fn from(value: Errno) -> Self { + syscall::Error::new(value.0) + } +} pub trait ResultExt { fn or_minus_one_errno(self) -> T; @@ -130,13 +136,15 @@ pub(crate) unsafe fn create( ) -> Result { let attrs = attrs.copied().unwrap_or_default(); - let mut procmask = 0_u64; + let mut current_sigmask = 0_u64; #[cfg(target_os = "redox")] - syscall::sigprocmask(syscall::SIG_SETMASK, None, Some(&mut procmask)) - .expect("failed to obtain sigprocmask for caller"); + { + current_sigmask = + redox_rt::signal::get_sigmask().expect("failed to obtain sigprocmask for caller"); + } // Create a locked mutex, unlocked by the thread after it has started. - let synchronization_mutex = Mutex::locked(procmask); + let synchronization_mutex = Mutex::locked(current_sigmask); let synchronization_mutex = &synchronization_mutex; let stack_size = attrs.stacksize.next_multiple_of(Sys::getpagesize()); @@ -242,8 +250,10 @@ unsafe extern "C" fn new_thread_shim( (&*mutex).manual_unlock(); #[cfg(target_os = "redox")] - syscall::sigprocmask(syscall::SIG_SETMASK, Some(&procmask), None) - .expect("failed to set procmask in child thread"); + { + redox_rt::signal::set_sigmask(Some(procmask), None) + .expect("failed to set procmask in child thread"); + } let retval = entry_point(arg); @@ -316,8 +326,8 @@ unsafe fn dealloc_thread(thread: &Pthread) { OS_TID_TO_PTHREAD.lock().remove(&thread.os_tid.get().read()); //drop(Box::from_raw(thread as *const Pthread as *mut Pthread)); } -pub const SIGRT_RLCT_CANCEL: usize = 32; -pub const SIGRT_RLCT_TIMER: usize = 33; +pub const SIGRT_RLCT_CANCEL: usize = 33; +pub const SIGRT_RLCT_TIMER: usize = 34; unsafe extern "C" fn cancel_sighandler(_: c_int) { cancel_current_thread(); diff --git a/src/start.rs b/src/start.rs index 13528e99b0..f17bd48723 100644 --- a/src/start.rs +++ b/src/start.rs @@ -3,7 +3,7 @@ use core::{intrinsics, ptr}; use crate::{ header::{libgen, stdio, stdlib}, - ld_so::{self, linker::Linker}, + ld_so::{self, linker::Linker, tcb::Tcb}, platform::{self, get_auxvs, types::*, Pal, Sys}, sync::mutex::Mutex, ALLOCATOR, @@ -157,6 +157,8 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! { //TODO: load root object tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker))); } + #[cfg(target_os = "redox")] + redox_rt::signal::setup_sighandler(&tcb.os_specific); } // Set up argc and argv @@ -182,8 +184,6 @@ pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! { platform::OUR_ENVIRON = copy_string_array(envp, len); platform::environ = platform::OUR_ENVIRON.as_mut_ptr(); } - #[cfg(target_os = "redox")] - platform::sys::signal::setup_sighandler(); let auxvs = get_auxvs(sp.auxv().cast()); crate::platform::init(auxvs); diff --git a/tests/Makefile b/tests/Makefile index 46690fd8f2..c27e6d8573 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -157,6 +157,7 @@ NAMES=\ $(EXPECT_NAMES) \ dirent/main \ pwd \ + sigchld \ stdio/tempnam \ stdio/tmpnam \ stdlib/bsearch \ diff --git a/tests/sigchld.c b/tests/sigchld.c new file mode 100644 index 0000000000..455f9981df --- /dev/null +++ b/tests/sigchld.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +volatile sig_atomic_t atomic = 0; +volatile sig_atomic_t atomic2 = 0; + +void action(int sig, siginfo_t *info, void *context) { + (void)context; + + assert(sig == SIGCHLD); + assert(info != NULL); + atomic += 1; +} +void handler(int sig) { + assert(sig == SIGPIPE); + atomic2 += 1; +} + +int main(void) { + int fds[2]; + int status = pipe(fds); + ERROR_IF(pipe, status, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + sigset_t set; + sigemptyset(&set); + status = sigprocmask(SIG_SETMASK, &set, NULL); + ERROR_IF(sigprocmask, status, == -1); + + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_sigaction = action; + status = sigaction(SIGCHLD, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + sa.sa_handler = handler; + status = sigaction(SIGPIPE, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + if (child == 0) { + status = close(fds[1]); + ERROR_IF(close, status, == -1); + + char buf[1]; + status = read(fds[0], buf, 1); + ERROR_IF(read, status, == -1); + puts("Child exiting."); + return EXIT_SUCCESS; + } else { + int waitpid_stat; + + close(fds[0]); + ERROR_IF(close, status, == -1); + + puts("Sending SIGSTOP..."); + status = kill(child, SIGSTOP); + ERROR_IF(kill, status, == -1); + + while (atomic == 0) { + status = sched_yield(); + ERROR_IF(sched_yield, status, == -1); + } + puts("First handler ran, checking status."); + + status = waitpid(child, &waitpid_stat, WUNTRACED); + ERROR_IF(waitpid, status, == -1); + assert(WIFSTOPPED(waitpid_stat)); + assert(WSTOPSIG(waitpid_stat) == SIGSTOP); + puts("Correct, sending SIGCONT..."); + + status = write(fds[1], "C", 1); + ERROR_IF(write, status, == -1); + + status = kill(child, SIGCONT); + ERROR_IF(kill, status, == -1); + + while (atomic == 1) { + status = sched_yield(); + ERROR_IF(sched_yield, status, == -1); + } + puts("Second handler ran, checking status."); + + status = waitpid(child, &waitpid_stat, 0); + ERROR_IF(waitpid, status, == -1); + assert(WIFEXITED(waitpid_stat)); + assert(WEXITSTATUS(waitpid_stat) == 0); + puts("Child exited."); + + puts("Writing to (broken) pipe."); + status = write(fds[1], "B", 1); + ERROR_IF(write, status, != -1); + assert(errno == EPIPE); + + while (atomic2 == 0) { + status = sched_yield(); + ERROR_IF(sched_yield, status, == -1); + } + puts("SIGSTOP handler successfully executed."); + return EXIT_SUCCESS; + } +}