Merge branch 'usignal' of gitlab.redox-os.org:4lDO2/relibc

This commit is contained in:
Jeremy Soller
2024-07-15 09:38:36 -06:00
43 changed files with 2433 additions and 1093 deletions
Generated
+31 -34
View File
@@ -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",
+5 -4
View File
@@ -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"] }
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "generic-rt"
version = "0.1.0"
edition = "2021"
[dependencies]
+112
View File
@@ -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<Os> {
/// 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<Os> GenericTcb<Os> {
/// 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::<Self>() {
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<T, E: core::fmt::Debug> ExpectTlsFree for Result<T, E> {
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<T> ExpectTlsFree for Option<T> {
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)),
}
}
}
+14
View File
@@ -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
@@ -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" }
+292
View File
@@ -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) {}
+281
View File
@@ -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
}
@@ -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::*;
+402
View File
@@ -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
}
+124
View File
@@ -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<RtSigarea>;
/// 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);
}
}
@@ -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<usize> {
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<usize> {
pub fn fork_inner(initial_rsp: *mut usize) -> Result<usize> {
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<usize> {
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<usize> {
);
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
+496
View File
@@ -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<u64> {
let mut mask = 0;
modify_sigmask(Some(&mut mask), Option::<fn(u32, bool) -> u32>::None)?;
Ok(mask)
}
pub fn set_sigmask(new: Option<u64>, 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<u64>, 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<u64>, 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<impl FnMut(u32, bool) -> 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 = &current_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 = &current_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<extern "C" fn(c_int)>,
pub sigaction: Option<unsafe extern "C" fn(c_int, *const (), *mut ())>,
}
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<crate::arch::SigArea>,
}
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)
}
+52
View File
@@ -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<T> {
pub lockword: AtomicU32,
pub inner: UnsafeCell<T>,
}
const UNLOCKED: u32 = 0;
const LOCKED: u32 = 1;
const WAITING: u32 = 2;
unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}
impl<T> Mutex<T> {
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<T>,
}
impl<T> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.inner.get() }
}
}
impl<T> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.inner.get() }
}
}
impl<T> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
self.lock.lockword.store(UNLOCKED, Ordering::Release);
}
}
+43
View File
@@ -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<usize>) -> Result<usize> {
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<usize> {
wrapper(|| syscall::read(fd, buf))
}
#[inline]
pub fn posix_write(fd: usize, buf: &[u8]) -> Result<usize> {
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),
}
}
+49
View File
@@ -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<usize> {
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)
}
+2 -1
View File
@@ -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 <bits/signal.h>"
language = "C"
@@ -15,3 +15,4 @@ prefix_with_name = true
[export.rename]
"timespec" = "struct timespec"
"sigaction" = "struct sigaction"
+2 -1
View File
@@ -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;
+38 -37
View File
@@ -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<extern "C" fn(c_int)>,
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<extern "C" fn(c_int)> {
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);
+9 -42
View File
@@ -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;
+3 -3
View File
@@ -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 => {
+2 -2
View File
@@ -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;
+2 -35
View File
@@ -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<T, E: core::fmt::Debug> ExpectTlsFree for Result<T, E> {
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<T> ExpectTlsFree for Option<T> {
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;
+45 -116
View File
@@ -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<OsSpecific>,
/// 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::<Master>())
@@ -55,21 +62,31 @@ pub struct Tcb {
pub pthread: Pthread,
}
#[cfg(target_os = "redox")]
const _: () = {
if mem::size_of::<Tcb>() > 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::<Self>() {
None
} else {
Some(&mut *tcb_ptr)
}
Some(&mut *GenericTcb::<OsSpecific>::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<OsSpecific>;
fn deref(&self) -> &Self::Target {
&self.generic
}
}
impl DerefMut for Tcb {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.generic
}
}
-2
View File
@@ -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;
-19
View File
@@ -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();
}
}
);
+57 -21
View File
@@ -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::<sigset_t>()
)
}) 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::<sigset_t>()
)
})
.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::<sigset_t>()
)) 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::<sigset_t>()
)
})
.map(|_| ())
}
unsafe fn sigsuspend(set: *const sigset_t) -> c_int {
+2 -2
View File
@@ -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]]>) {}
+19 -8
View File
@@ -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;
+3 -155
View File
@@ -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<usize> {
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::*;
+5 -6
View File
@@ -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<redox_exec::InterpOverride>,
interp_override: Option<InterpOverride>,
) -> Result<usize> {
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<redox_exec::InterpOverride>,
interp_override: Option<InterpOverride>,
) -> Result<usize> {
// 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),
+1 -1
View File
@@ -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 {
+21 -9
View File
@@ -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<usize> {
@@ -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(
+7 -5
View File
@@ -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<ssize_t, Errno> {
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<ssize_t, Errno> {
unsafe {
@@ -1124,7 +1125,8 @@ impl Pal for Sys {
}
fn write(fd: c_int, buf: &[u8]) -> Result<ssize_t, Errno> {
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<ssize_t, Errno> {
unsafe {
+6 -25
View File
@@ -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<Box<str>> {
let _siglock = SignalMask::lock();
let _siglock = tmp_disable_signals();
CWD.lock().clone()
}
// TODO: MaybeUninit
pub fn getcwd(buf: &mut [u8]) -> Option<usize> {
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<usize> {
Some(cwd.len())
}
// TODO: Move cwd from kernel to libc. It is passed via auxiliary vectors.
pub fn canonicalize(path: &str) -> Result<String> {
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<String> {
static CWD: Mutex<Option<Box<str>>> = Mutex::new(None);
pub fn setcwd_manual(cwd: Box<str>) {
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<usize> {
// TODO: SYMLOOP_MAX
const MAX_LEVEL: usize = 64;
@@ -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();
}
@@ -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();
}
@@ -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();
}
+134 -183
View File
@@ -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]);
+18 -8
View File
@@ -92,6 +92,12 @@ impl From<syscall::Error> for Errno {
Errno(value.errno)
}
}
#[cfg(target_os = "redox")]
impl From<Errno> for syscall::Error {
fn from(value: Errno) -> Self {
syscall::Error::new(value.0)
}
}
pub trait ResultExt<T> {
fn or_minus_one_errno(self) -> T;
@@ -130,13 +136,15 @@ pub(crate) unsafe fn create(
) -> Result<pthread_t, Errno> {
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();
+3 -3
View File
@@ -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);
+1
View File
@@ -157,6 +157,7 @@ NAMES=\
$(EXPECT_NAMES) \
dirent/main \
pwd \
sigchld \
stdio/tempnam \
stdio/tmpnam \
stdlib/bsearch \
+113
View File
@@ -0,0 +1,113 @@
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <sched.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#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;
}
}