diff --git a/Cargo.lock b/Cargo.lock
index e485d476b7..b24e0eda2c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -227,9 +227,9 @@ version = "0.1.0"
[[package]]
name = "libc"
-version = "0.2.171"
+version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libm"
@@ -377,9 +377,9 @@ version = "0.1.2"
[[package]]
name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
@@ -458,7 +458,7 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.11"
-source = "git+https://gitlab.redox-os.org/redox-os/syscall.git#080f2003cd065bcc9a290230ede06da0a1ea0502"
+source = "git+https://gitlab.redox-os.org/redox-os/syscall.git?branch=master#8f646a63ac9c3aa532a37ddfdc136ec19878f0d6"
dependencies = [
"bitflags",
]
diff --git a/Cargo.toml b/Cargo.toml
index ebe3371f8e..8846727afb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -81,4 +81,4 @@ panic = "abort"
[patch.crates-io]
cc-11 = { git = "https://github.com/tea/cc-rs", branch = "riscv-abi-arch-fix", package = "cc" }
-redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" }
+redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master" }
diff --git a/README.md b/README.md
index 68bff0533e..27ee65dfbf 100644
--- a/README.md
+++ b/README.md
@@ -175,3 +175,10 @@ Before starting to contribute, read [this](CONTRIBUTING.md) document.
- i686 (Intel/AMD)
- x86_64 (Intel/AMD)
- Aarch64 (ARM64)
+
+## Funding - _Unix-style Signals and Process Management_
+
+This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals).
+
+[
](https://nlnet.nl)
+[
](https://nlnet.nl/core)
diff --git a/include/bits/sys/wait.h b/include/bits/sys/wait.h
index 7af6a82bbe..c76dbcab95 100644
--- a/include/bits/sys/wait.h
+++ b/include/bits/sys/wait.h
@@ -2,7 +2,7 @@
#define _BITS_SYS_WAIT_H
#define WEXITSTATUS(s) (((s) >> 8) & 0xff)
-#define WTERMSIG(s) (((s) & 0x7f) != 0)
+#define WTERMSIG(s) ((s) & 0x7f)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WCOREDUMP(s) (((s) & 0x80) != 0)
#define WIFEXITED(s) (((s) & 0x7f) == 0)
diff --git a/redox-rt/Cargo.toml b/redox-rt/Cargo.toml
index a69851747e..9210ba259a 100644
--- a/redox-rt/Cargo.toml
+++ b/redox-rt/Cargo.toml
@@ -15,3 +15,7 @@ plain = "0.2"
redox_syscall = "0.5.8"
generic-rt = { path = "../generic-rt" }
+
+[features]
+proc = []
+default = ["proc"]
diff --git a/redox-rt/src/arch/aarch64.rs b/redox-rt/src/arch/aarch64.rs
index 0e4dbbc76a..de30ce4732 100644
--- a/redox-rt/src/arch/aarch64.rs
+++ b/redox-rt/src/arch/aarch64.rs
@@ -1,9 +1,10 @@
-use core::{mem::offset_of, ptr::NonNull};
+use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull};
use syscall::{data::*, error::*};
use crate::{
- proc::{fork_inner, FdGuard},
+ proc::{fork_inner, FdGuard, ForkArgs},
+ protocol::{ProcCall, RtSigInfo},
signal::{inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT},
RtTcb, Tcb,
};
@@ -20,6 +21,7 @@ pub struct SigArea {
pub tmp_x1_x2: [usize; 2],
pub tmp_x3_x4: [usize; 2],
pub tmp_x5_x6: [usize; 2],
+ pub tmp_x7_x8: [usize; 2],
pub tmp_sp: usize,
pub onstack: u64,
pub disable_signals_depth: u64,
@@ -97,20 +99,24 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
Ok(())
}
-unsafe extern "C" fn fork_impl(initial_rsp: *mut usize) -> usize {
- Error::mux(fork_inner(initial_rsp))
+unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
+ Error::mux(fork_inner(initial_rsp, args))
}
-unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
+unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_proc_fd: usize, new_thr_fd: usize) {
+ //let _ = syscall::write(1, alloc::format!("CUR{cur_filetable_fd}PROC{new_proc_fd}THR{new_thr_fd}\n").as_bytes());
let _ = syscall::close(cur_filetable_fd);
- // TODO: Currently pidfd == threadfd, but this will not be the case later.
- RtTcb::current()
- .thr_fd
- .get()
- .write(Some(FdGuard::new(new_pid_fd)));
+ crate::child_hook_common(crate::ChildHookCommonArgs {
+ new_thr_fd: FdGuard::new(new_thr_fd),
+ new_proc_fd: if new_proc_fd == usize::MAX {
+ None
+ } else {
+ Some(FdGuard::new(new_proc_fd))
+ },
+ });
}
-asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
+asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
stp x29, x30, [sp, #-16]!
stp x27, x28, [sp, #-16]!
stp x25, x26, [sp, #-16]!
@@ -122,7 +128,8 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
//TODO: store floating point regs
- mov x0, sp
+ // x0: &ForkArgs
+ mov x1, sp
bl {fork_impl}
add sp, sp, #32
@@ -136,14 +143,14 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
"] <= [fork_impl = sym fork_impl]);
asmfunction!(__relibc_internal_fork_ret: ["
- ldp x0, x1, [sp]
+ ldp x0, x1, [sp], #16
+ ldp x2, x3, [sp], #16
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
@@ -167,6 +174,7 @@ asmfunction!(__relibc_internal_sigentry: ["
stp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
stp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
stp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
+ stp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}]
mov x1, sp
str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}]
@@ -219,37 +227,47 @@ asmfunction!(__relibc_internal_sigentry: ["
clrex
// Load the pending set again. TODO: optimize this?
+ // Process pending - realtime
add x1, x6, #{pctl_pending}
ldaxr x2, [x1]
lsr x2, x2, #32
+ // Thread pending - realtime and allowset
add x5, x0, #{tcb_sc_off} + {sc_word} + 8
ldar x1, [x5]
- orr x2, x1, x2
- and x2, x2, x2, lsr #32
- cbz x2, 7f
+ orr x2, x1, x2 // combine proc and thread pending
+ and x2, x2, x2, lsr #32 // AND pending with allowset
+ cbz x2, 7f // spurious signal if realtime is clear
- rbit x3, x2
- clz x3, x3
- mov x4, #31
- sub x2, x4, x3
+ rbit x2, x2
+ clz x2, x2
// x2 now contains sig_idx - 32
// If realtime signal was directed at thread, handle it as an idempotent signal.
- lsr x3, x1, x2
- tbnz x3, #0, 5f
+ lsr x3, x1, x2 // x3 := x1 >> x2; x1 is thread pending
+ tbnz x3, #0, 5f // jump if bit is nonzero
- mov x5, x0
- mov x4, x8
- mov x8, #{SYS_SIGDEQUEUE}
- mov x0, x1
- add x1, x0, #{tcb_sa_off} + {sa_tmp_rt_inf}
+ // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base | (flags << 8))
+ // x8 x0 x1 x2 x3 x4
+
+ mov x5, x0 // save TCB pointer
+ mov x6, x2
+ ldr x8, ={SYS_CALL}
+ adrp x0, {proc_fd}
+ ldr x0, [x0, #:lo12:{proc_fd}]
+ add x1, x5, #{tcb_sa_off} + {sa_tmp_rt_inf}
+ str x6, [x1]
+ mov x2, #{RTINF_SIZE}
+ adrp x4, {proc_call}
+ add x4, x4, :lo12:{proc_call}
+ mov x3, #1
svc 0
- mov x0, x5
- mov x8, x4
- cbnz x0, 1b
-
+ mov x3, x0
+ mov x0, x5 // restore TCB pointer
+ mov x2, x6 // restore signal number - 32
+ add x1, x2, #32 // signal number
+ cbnz x3, 1b
b 2f
5:
// A realtime signal was sent to this thread, try clearing its bit.
@@ -278,7 +296,7 @@ asmfunction!(__relibc_internal_sigentry: ["
b 2f
3:
// A standard signal was sent to this thread, try clearing its bit.
- clz x1, x1
+ clz w1, w1
mov x2, #31
sub x1, x2, x1
@@ -299,7 +317,7 @@ asmfunction!(__relibc_internal_sigentry: ["
str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}]
2:
ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}]
- add x2, x2, {pctl_actions}
+ add x3, x3, {pctl_actions}
add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action
// TODO: NOT ATOMIC (tearing allowed between regs)!
ldxp x2, x3, [x2]
@@ -328,8 +346,9 @@ asmfunction!(__relibc_internal_sigentry: ["
stp x4, x3, [sp, #-16]!
ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
stp x6, x5, [sp, #-16]!
-
+ ldp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}]
stp x8, x7, [sp, #-16]!
+
stp x10, x9, [sp, #-16]!
stp x12, x11, [sp, #-16]!
stp x14, x13, [sp, #-16]!
@@ -400,6 +419,7 @@ asmfunction!(__relibc_internal_sigentry: ["
sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2),
sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4),
sa_tmp_x5_x6 = const offset_of!(SigArea, tmp_x5_x6),
+ sa_tmp_x7_x8 = const offset_of!(SigArea, tmp_x7_x8),
sa_tmp_sp = const offset_of!(SigArea, tmp_sp),
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
@@ -409,12 +429,17 @@ asmfunction!(__relibc_internal_sigentry: ["
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
sc_word = const offset_of!(Sigcontrol, word),
sc_flags = const offset_of!(Sigcontrol, control_flags),
+ proc_fd = sym PROC_FD,
inner = sym inner_c,
+ proc_call = sym PROC_CALL,
SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000
- SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE,
+
+ SYS_CALL = const syscall::SYS_CALL,
+
STACK_ALIGN = const 16,
REDZONE_SIZE = const 128,
+ RTINF_SIZE = const size_of::(),
]);
asmfunction!(__relibc_internal_rlct_clone_ret: ["
@@ -460,3 +485,5 @@ pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) -> PosixStackt {
flags: 0, // TODO
}
}
+pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX);
+static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
diff --git a/redox-rt/src/arch/i686.rs b/redox-rt/src/arch/i686.rs
index 71319f50d5..b089a607bb 100644
--- a/redox-rt/src/arch/i686.rs
+++ b/redox-rt/src/arch/i686.rs
@@ -1,9 +1,10 @@
-use core::{mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
+use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
use syscall::*;
use crate::{
- proc::{fork_inner, FdGuard},
+ proc::{fork_inner, FdGuard, ForkArgs},
+ protocol::{ProcCall, RtSigInfo},
signal::{inner_fastcall, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT},
RtTcb,
};
@@ -20,12 +21,15 @@ pub struct SigArea {
pub tmp_eip: usize,
pub tmp_esp: usize,
pub tmp_eax: usize,
+ pub tmp_ebx: usize,
pub tmp_ecx: usize,
pub tmp_edx: usize,
+ pub tmp_edi: usize,
+ pub tmp_esi: usize,
pub tmp_rt_inf: RtSigInfo,
+ pub tmp_signo: usize,
pub tmp_id_inf: u64,
pub tmp_mm0: u64,
- pub pctl: usize, // TODO: reference pctl directly
pub disable_signals_depth: u64,
pub last_sig_was_restart: bool,
pub last_sigstack: Option>,
@@ -79,16 +83,30 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
Ok(())
}
-unsafe extern "cdecl" fn fork_impl(initial_rsp: *mut usize) -> usize {
- Error::mux(fork_inner(initial_rsp))
+unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
+ Error::mux(fork_inner(initial_rsp, args))
}
-unsafe extern "cdecl" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
+// TODO: duplicate code with x86_64
+unsafe extern "cdecl" fn child_hook(
+ cur_filetable_fd: usize,
+ new_proc_fd: usize,
+ new_thr_fd: usize,
+) {
let _ = syscall::close(cur_filetable_fd);
- crate::child_hook_common(FdGuard::new(new_pid_fd));
+ crate::child_hook_common(crate::ChildHookCommonArgs {
+ new_thr_fd: FdGuard::new(new_thr_fd),
+ new_proc_fd: if new_proc_fd == usize::MAX {
+ None
+ } else {
+ Some(FdGuard::new(new_proc_fd))
+ },
+ });
}
-asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
+asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
+ mov ecx, [esp+4]
+
push ebp
mov ebp, esp
@@ -103,9 +121,9 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
//TODO stmxcsr [esp+16]
fnstcw [esp+24]
- push esp
+ mov edx, esp
call {fork_impl}
- pop esp
+
jmp 2f
"] <= [fork_impl = sym fork_impl]);
@@ -137,6 +155,9 @@ asmfunction!(__relibc_internal_sigentry: ["
mov gs:[{tcb_sa_off} + {sa_tmp_eax}], eax
mov gs:[{tcb_sa_off} + {sa_tmp_edx}], edx
mov gs:[{tcb_sa_off} + {sa_tmp_ecx}], ecx
+ mov gs:[{tcb_sa_off} + {sa_tmp_ebx}], ebx
+ mov gs:[{tcb_sa_off} + {sa_tmp_edi}], edi
+ mov gs:[{tcb_sa_off} + {sa_tmp_esi}], esi
1:
// Read standard signal word - first for this thread
mov edx, gs:[{tcb_sc_off} + {sc_word} + 4]
@@ -145,9 +166,8 @@ asmfunction!(__relibc_internal_sigentry: ["
bsf eax, eax
jnz 9f
- mov ecx, gs:[{tcb_sa_off} + {sa_pctl}]
-
// Read standard signal word - for the process
+ lea ecx, [{pctl}]
mov eax, [ecx + {pctl_pending}]
and eax, edx
bsf eax, eax
@@ -169,24 +189,31 @@ asmfunction!(__relibc_internal_sigentry: ["
mov eax, gs:[{tcb_sc_off} + {sc_word} + 8]
or eax, edx
and eax, gs:[{tcb_sc_off} + {sc_word} + 12]
- jz 7f // spurious signal
bsf eax, eax
+ jz 7f // spurious signal
- // If thread was specifically targeted, send the signal to it first.
+ // If thread rather than process was specifically targeted, send the signal to it first.
bt edx, eax
- jc 8f
+ jnc 8f
- mov edx, ebx
- lea ecx, [eax+32]
- mov eax, {SYS_SIGDEQUEUE}
- mov edx, gs:[0]
- add edx, {tcb_sa_off} + {sa_tmp_rt_inf}
+ // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
+ // eax ebx ecx edx esi edi
+
+ mov ebx, [{proc_fd}]
+ mov ecx, gs:[0]
+ add ecx, {tcb_sa_off} + {sa_tmp_rt_inf}
+ mov [ecx], eax
+ mov gs:[{tcb_sa_off} + {sa_tmp_signo}], eax
+ mov edx, {RTINF_SIZE}
+ mov esi, 1
+ lea edi, [{proc_call}]
+ mov eax, {SYS_CALL}
int 0x80
- mov ebx, edx
test eax, eax
jnz 1b
- mov eax, ecx
+ mov eax, gs:[{tcb_sa_off} + {sa_tmp_signo}]
+ add eax, 32
jmp 2f
8:
add eax, 32
@@ -228,9 +255,9 @@ asmfunction!(__relibc_internal_sigentry: ["
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}]
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ecx}]
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}]
- push ebx
- push edi
- push esi
+ push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ebx}]
+ push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edi}]
+ push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esi}]
push ebp
sub esp, 2 * 4 + 29 * 16
@@ -274,8 +301,11 @@ __relibc_internal_sigentry_crit_second:
mov gs:[{tcb_sa_off} + {sa_tmp_eip}], eax
mov eax, gs:[{tcb_sa_off} + {sa_tmp_eax}]
+ mov ebx, gs:[{tcb_sa_off} + {sa_tmp_ebx}]
mov ecx, gs:[{tcb_sa_off} + {sa_tmp_ecx}]
mov edx, gs:[{tcb_sa_off} + {sa_tmp_edx}]
+ mov edi, gs:[{tcb_sa_off} + {sa_tmp_edi}]
+ mov esi, gs:[{tcb_sa_off} + {sa_tmp_esi}]
and dword ptr gs:[{tcb_sc_off} + {sc_control}], ~1
.globl __relibc_internal_sigentry_crit_third
@@ -286,14 +316,17 @@ __relibc_internal_sigentry_crit_third:
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_ebx = const offset_of!(SigArea, tmp_ebx),
sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx),
sa_tmp_edx = const offset_of!(SigArea, tmp_edx),
+ sa_tmp_edi = const offset_of!(SigArea, tmp_edi),
+ sa_tmp_esi = const offset_of!(SigArea, tmp_esi),
sa_tmp_mm0 = const offset_of!(SigArea, tmp_mm0),
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
+ sa_tmp_signo = const offset_of!(SigArea, tmp_signo),
sa_altstack_top = const offset_of!(SigArea, altstack_top),
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
- sa_pctl = const offset_of!(SigArea, pctl),
sc_control = const offset_of!(Sigcontrol, control_flags),
sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg),
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
@@ -305,8 +338,11 @@ __relibc_internal_sigentry_crit_third:
pctl_sender_infos = const offset_of!(SigProcControl, sender_infos),
pctl_pending = const offset_of!(SigProcControl, pending),
pctl = sym PROC_CONTROL_STRUCT,
+ proc_fd = sym PROC_FD,
+ proc_call = sym PROC_CALL,
STACK_ALIGN = const 16,
- SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE,
+ SYS_CALL = const syscall::SYS_CALL,
+ RTINF_SIZE = const size_of::(),
]);
asmfunction!(__relibc_internal_rlct_clone_ret -> usize: ["
@@ -377,3 +413,6 @@ pub fn current_sp() -> usize {
}
sp
}
+
+pub static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX);
+static PROC_CALL: u64 = ProcCall::Sigdeq as u64;
diff --git a/redox-rt/src/arch/riscv64.rs b/redox-rt/src/arch/riscv64.rs
index 3faa17118e..22b8c20008 100644
--- a/redox-rt/src/arch/riscv64.rs
+++ b/redox-rt/src/arch/riscv64.rs
@@ -1,5 +1,8 @@
+use core::cell::SyncUnsafeCell;
+
use crate::{
- proc::{fork_inner, FdGuard},
+ proc::{fork_inner, FdGuard, ForkArgs},
+ protocol::{ProcCall, RtSigInfo},
signal::{get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack},
RtTcb, Tcb,
};
@@ -21,6 +24,8 @@ pub struct SigArea {
pub tmp_a0: u64,
pub tmp_a1: u64,
pub tmp_a2: u64,
+ pub tmp_a3: u64,
+ pub tmp_a4: u64,
pub tmp_a7: u64,
pub pctl: usize, // TODO: remove
@@ -70,16 +75,23 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
Ok(())
}
-unsafe extern "C" fn fork_impl(initial_rsp: *mut usize) -> usize {
- Error::mux(fork_inner(initial_rsp))
+unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
+ Error::mux(fork_inner(initial_rsp, args))
}
-unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
+unsafe extern "C" fn child_hook(cur_filetable_fd: usize, new_proc_fd: usize, new_thr_fd: usize) {
let _ = syscall::close(cur_filetable_fd);
- crate::child_hook_common(FdGuard::new(new_pid_fd));
+ crate::child_hook_common(crate::ChildHookCommonArgs {
+ new_thr_fd: FdGuard::new(new_thr_fd),
+ new_proc_fd: if new_proc_fd == usize::MAX {
+ None
+ } else {
+ Some(FdGuard::new(new_proc_fd))
+ },
+ });
}
-asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
+asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
.attribute arch, \"rv64gc\" # rust bug 80608
addi sp, sp, -200
sd s0, 0(sp)
@@ -110,7 +122,8 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
fsd fs11, 192(sp)
addi sp, sp, -32
- mv a0, sp
+ // a0 is forwarded from this function
+ mv a1, sp
jal {fork_impl}
addi sp, sp, 32
@@ -148,8 +161,9 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
asmfunction!(__relibc_internal_fork_ret: ["
.attribute arch, \"rv64gc\" # rust bug 80608
- ld a0, 0(sp)
- ld a1, 8(sp)
+ ld a0, 0(sp) // cur_filetable_fd
+ ld a1, 8(sp) // new_proc_fd
+ ld a2, 16(sp) // new_thr_fd
jal {child_hook}
mv a0, x0
@@ -259,14 +273,30 @@ asmfunction!(__relibc_internal_sigentry: ["
bnez t1, 10f // thread signal
// otherwise, try (competitively) dequeueing realtime signal
+
+ // SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
+ // a7 a0 a1 a2 a3 a4
+
+ // TODO: This SYS_CALL invocation has not yet been tested due to toolchain issues.
+
sd a0, ({tcb_sa_off} + {sa_tmp_a0})(t0)
sd a1, ({tcb_sa_off} + {sa_tmp_a1})(t0)
sd a2, ({tcb_sa_off} + {sa_tmp_a2})(t0)
+ sd a3, ({tcb_sa_off} + {sa_tmp_a3})(t0)
+ sd a4, ({tcb_sa_off} + {sa_tmp_a4})(t0)
sd a7, ({tcb_sa_off} + {sa_tmp_a7})(t0)
- li a0, {SYS_SIGDEQUEUE}
- addi a1, t3, -32
- add a2, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
+ li a7, {SYS_CALL}
+ addi a2, t3, -32
+ add a1, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
+ sd a2, (a1)
+ li a2, {RTINF_SIZE}
+ li a3, 1
+1337:
+ auipc a4, %pcrel_hi({proc_fd})
+ addi a4, a4, %pcrel_lo(1337b)
ecall
+ ld a3, ({tcb_sa_off} + {sa_tmp_a3})(t0)
+ ld a4, ({tcb_sa_off} + {sa_tmp_a4})(t0)
bnez a0, 99b // assumes error can only be EAGAIN
j 9f
@@ -520,6 +550,8 @@ __relibc_internal_sigentry_crit_fifth:
sa_tmp_a0 = const offset_of!(SigArea, tmp_a0),
sa_tmp_a1 = const offset_of!(SigArea, tmp_a1),
sa_tmp_a2 = const offset_of!(SigArea, tmp_a2),
+ sa_tmp_a3 = const offset_of!(SigArea, tmp_a3),
+ sa_tmp_a4 = const offset_of!(SigArea, tmp_a4),
sa_tmp_a7 = const offset_of!(SigArea, tmp_a7),
sa_tmp_ip = const offset_of!(SigArea, tmp_ip),
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
@@ -531,7 +563,9 @@ __relibc_internal_sigentry_crit_fifth:
inner = sym inner_c,
pctl_off_pending = const offset_of!(SigProcControl, pending),
pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos),
- SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE,
+ SYS_CALL = const syscall::SYS_CALL,
+ RTINF_SIZE = const size_of::(),
+ proc_fd = sym PROC_FD,
]);
asmfunction!(__relibc_internal_rlct_clone_ret: ["
@@ -620,3 +654,5 @@ pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt
get_sigaltstack(area, stack.regs.int_regs[1] as usize).into()
}
+pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX);
+static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs
index cf9d69388a..3a9f1b0166 100644
--- a/redox-rt/src/arch/x86_64.rs
+++ b/redox-rt/src/arch/x86_64.rs
@@ -1,4 +1,5 @@
use core::{
+ cell::SyncUnsafeCell,
mem::offset_of,
ptr::NonNull,
sync::atomic::{AtomicU8, Ordering},
@@ -7,11 +8,11 @@ use core::{
use syscall::{
data::{SigProcControl, Sigcontrol},
error::*,
- RtSigInfo,
};
use crate::{
- proc::{fork_inner, FdGuard},
+ proc::{fork_inner, FdGuard, ForkArgs},
+ protocol::{ProcCall, RtSigInfo},
signal::{get_sigaltstack, inner_c, PosixStackt, RtSigarea, SigStack, PROC_CONTROL_STRUCT},
Tcb,
};
@@ -29,6 +30,9 @@ pub struct SigArea {
pub tmp_rdx: usize,
pub tmp_rdi: usize,
pub tmp_rsi: usize,
+ pub tmp_r8: usize,
+ pub tmp_r10: usize,
+ pub tmp_r12: usize,
pub tmp_rt_inf: RtSigInfo,
pub tmp_id_inf: u64,
@@ -92,16 +96,27 @@ pub fn copy_env_regs(cur_pid_fd: usize, new_pid_fd: usize) -> Result<()> {
Ok(())
}
-unsafe extern "sysv64" fn fork_impl(initial_rsp: *mut usize) -> usize {
- Error::mux(fork_inner(initial_rsp))
+unsafe extern "sysv64" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
+ Error::mux(fork_inner(initial_rsp, args))
}
-unsafe extern "sysv64" fn child_hook(cur_filetable_fd: usize, new_pid_fd: usize) {
+unsafe extern "sysv64" fn child_hook(
+ cur_filetable_fd: usize,
+ new_proc_fd: usize,
+ new_thr_fd: usize,
+) {
let _ = syscall::close(cur_filetable_fd);
- crate::child_hook_common(FdGuard::new(new_pid_fd));
+ crate::child_hook_common(crate::ChildHookCommonArgs {
+ new_thr_fd: FdGuard::new(new_thr_fd),
+ new_proc_fd: if new_proc_fd == usize::MAX {
+ None
+ } else {
+ Some(FdGuard::new(new_proc_fd))
+ },
+ });
}
-asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
+asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
push rbp
mov rbp, rsp
@@ -112,15 +127,16 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
push r14
push r15
- sub rsp, 32
+ sub rsp, 48
- stmxcsr [rsp+16]
- fnstcw [rsp+24]
+ stmxcsr [rsp+32]
+ fnstcw [rsp+40]
- mov rdi, rsp
+ // rdi: &ForkArgs
+ mov rsi, rsp
call {fork_impl}
- add rsp, 80
+ add rsp, 96
pop rbp
ret
@@ -129,14 +145,15 @@ asmfunction!(__relibc_internal_fork_wrapper -> usize: ["
asmfunction!(__relibc_internal_fork_ret: ["
mov rdi, [rsp]
mov rsi, [rsp + 8]
+ mov rdx, [rsp + 16]
call {child_hook}
- ldmxcsr [rsp + 16]
- fldcw [rsp + 24]
+ ldmxcsr [rsp + 32]
+ fldcw [rsp + 40]
xor rax, rax
- add rsp, 32
+ add rsp, 48
pop r15
pop r14
pop r13
@@ -175,6 +192,9 @@ asmfunction!(__relibc_internal_sigentry: ["
mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx
mov fs:[{tcb_sa_off} + {sa_tmp_rdi}], rdi
mov fs:[{tcb_sa_off} + {sa_tmp_rsi}], rsi
+ mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r8
+ mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r10
+ mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r12
// First, select signal, always pick first available bit
1:
@@ -212,14 +232,23 @@ asmfunction!(__relibc_internal_sigentry: ["
jc 2f // if so, continue as usual
// otherwise, try (competitively) dequeueing realtime signal
- mov esi, eax
- mov eax, {SYS_SIGDEQUEUE}
- mov rdi, fs:[0]
- add rdi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
+
+ // SYS_CALL(fd, payload_base, payload_len, metadata_len | (flags << 8), metadata_base)
+ // rax rdi rsi rdx r10 r8
+
+ mov r12d, eax
+ mov rsi, fs:[0]
+ mov rdi, [rip+{proc_fd}]
+ add rsi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
+ mov rdx, {RTINF_SIZE}
+ mov [rsi], eax
+ lea r8, [rip + {proc_call_sigdeq}]
+ mov r10, 1
+ mov eax, {SYS_CALL}
syscall
test eax, eax
jnz 1b // assumes error can only be EAGAIN
- lea eax, [esi + 32]
+ lea eax, [r12d + 32]
jmp 9f
2:
mov edx, eax
@@ -272,13 +301,13 @@ asmfunction!(__relibc_internal_sigentry: ["
push fs:[{tcb_sa_off} + {sa_tmp_rdx}]
push rcx
push fs:[{tcb_sa_off} + {sa_tmp_rax}]
- push r8
+ push fs:[{tcb_sa_off} + {sa_tmp_r8}]
push r9
- push r10
+ push fs:[{tcb_sa_off} + {sa_tmp_r10}]
push r11
push rbx
push rbp
- push r12
+ push fs:[{tcb_sa_off} + {sa_tmp_r12}]
push r13
push r14
push r15
@@ -405,6 +434,9 @@ __relibc_internal_sigentry_crit_third:
sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx),
sa_tmp_rdi = const offset_of!(SigArea, tmp_rdi),
sa_tmp_rsi = const offset_of!(SigArea, tmp_rsi),
+ sa_tmp_r8 = const offset_of!(SigArea, tmp_r8),
+ sa_tmp_r10 = const offset_of!(SigArea, tmp_r10),
+ sa_tmp_r12 = const offset_of!(SigArea, tmp_r12),
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
sa_altstack_top = const offset_of!(SigArea, altstack_top),
@@ -424,7 +456,10 @@ __relibc_internal_sigentry_crit_third:
REDZONE_SIZE = const 128,
STACK_ALIGN = const 16,
SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000
- SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE,
+ SYS_CALL = const syscall::SYS_CALL,
+ proc_call_sigdeq = sym PROC_CALL_SIGDEQ,
+ RTINF_SIZE = const size_of::(),
+ proc_fd = sym PROC_FD,
]);
extern "C" {
@@ -490,3 +525,6 @@ pub fn current_sp() -> usize {
}
sp
}
+
+static PROC_CALL_SIGDEQ: u64 = ProcCall::Sigdeq as u64;
+pub(crate) static PROC_FD: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX);
diff --git a/redox-rt/src/lib.rs b/redox-rt/src/lib.rs
index 1d0906a057..6e5beb3ef6 100644
--- a/redox-rt/src/lib.rs
+++ b/redox-rt/src/lib.rs
@@ -2,6 +2,7 @@
#![feature(
asm_const,
array_chunks,
+ core_intrinsics,
int_roundings,
let_chains,
slice_ptr_get,
@@ -9,18 +10,25 @@
)]
#![forbid(unreachable_patterns)]
-use core::cell::{SyncUnsafeCell, UnsafeCell};
+use core::{
+ cell::{SyncUnsafeCell, UnsafeCell},
+ mem::{size_of, MaybeUninit},
+};
use generic_rt::{ExpectTlsFree, GenericTcb};
-use syscall::{Sigcontrol, O_CLOEXEC};
+use syscall::Sigcontrol;
-use self::proc::FdGuard;
+use self::{
+ proc::{FdGuard, STATIC_PROC_INFO},
+ protocol::ProcMeta,
+ sync::Mutex,
+};
extern crate alloc;
#[macro_export]
macro_rules! asmfunction(
- ($name:ident $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => {
+ ($name:ident $(($($arg:ty),*))? $(-> $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
@@ -32,7 +40,7 @@ macro_rules! asmfunction(
"), $($decl = $(sym $symname)?$(const $constval)?),*);
extern "C" {
- pub fn $name() $(-> $ret)?;
+ pub fn $name($($(_: $arg),*)?) $(-> $ret)?;
}
}
);
@@ -44,6 +52,7 @@ pub mod proc;
#[path = "../../src/platform/auxv_defs.rs"]
pub mod auxv_defs;
+pub mod protocol;
pub mod signal;
pub mod sync;
pub mod sys;
@@ -60,14 +69,7 @@ impl RtTcb {
unsafe { &Tcb::current().unwrap().os_specific }
}
pub fn thread_fd(&self) -> &FdGuard {
- unsafe {
- if (&*self.thr_fd.get()).is_none() {
- self.thr_fd.get().write(Some(FdGuard::new(
- syscall::open("/scheme/thisproc/current/open_via_dup", O_CLOEXEC).unwrap(),
- )));
- }
- (&*self.thr_fd.get()).as_ref().unwrap()
- }
+ unsafe { (&*self.thr_fd.get()).as_ref().unwrap() }
}
}
@@ -134,7 +136,8 @@ pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) {
}
/// Initialize redox-rt in situations where relibc is not used
-pub unsafe fn initialize_freestanding() {
+#[cfg(not(feature = "proc"))]
+pub unsafe fn initialize_freestanding(this_thr_fd: FdGuard) -> &'static FdGuard {
// 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.
@@ -157,8 +160,9 @@ pub unsafe fn initialize_freestanding() {
page.tcb_ptr = page;
page.tcb_len = syscall::PAGE_SIZE;
page.tls_end = (page as *mut Tcb).cast();
+
// Make sure to use ptr::write to prevent dropping the existing FdGuard
- core::ptr::write(page.os_specific.thr_fd.get(), None);
+ page.os_specific.thr_fd.get().write(Some(this_thr_fd));
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
unsafe {
@@ -176,19 +180,124 @@ pub unsafe fn initialize_freestanding() {
core::arch::asm!("mv tp, {}", in(reg) (abi_ptr + 8));
}
initialize();
+
+ (*page.os_specific.thr_fd.get()).as_ref().unwrap()
}
-pub unsafe fn initialize() {
- THIS_PID
- .get()
- .write(Some(syscall::getpid().unwrap().try_into().unwrap()).unwrap());
+pub(crate) fn read_proc_meta(proc: &FdGuard) -> syscall::Result {
+ let mut bytes = [0_u8; size_of::()];
+ let _ = syscall::read(**proc, &mut bytes)?;
+ Ok(*plain::from_bytes::(&bytes).unwrap())
+}
+pub unsafe fn initialize(#[cfg(feature = "proc")] proc_fd: FdGuard) {
+ #[cfg(feature = "proc")]
+ let metadata = read_proc_meta(&proc_fd).unwrap();
+
+ #[cfg(not(feature = "proc"))]
+ // Bootstrap mode, don't associate proc fds with PIDs
+ let metadata = ProcMeta::default();
+
+ #[cfg(feature = "proc")]
+ {
+ crate::arch::PROC_FD.get().write(*proc_fd);
+ }
+
+ STATIC_PROC_INFO.get().write(StaticProcInfo {
+ pid: metadata.pid,
+
+ #[cfg(feature = "proc")]
+ proc_fd: MaybeUninit::new(proc_fd),
+
+ #[cfg(not(feature = "proc"))]
+ proc_fd: MaybeUninit::uninit(),
+
+ has_proc_fd: cfg!(feature = "proc"),
+ });
+
+ #[cfg(feature = "proc")]
+ {
+ *DYNAMIC_PROC_INFO.lock() = DynamicProcInfo {
+ pgid: metadata.pgid,
+ ruid: metadata.ruid,
+ euid: metadata.euid,
+ suid: metadata.suid,
+ egid: metadata.egid,
+ rgid: metadata.rgid,
+ sgid: metadata.sgid,
+ };
+ }
}
-static THIS_PID: SyncUnsafeCell = SyncUnsafeCell::new(0);
-
-unsafe fn child_hook_common(new_pid_fd: FdGuard) {
- // TODO: Currently pidfd == threadfd, but this will not be the case later.
- RtTcb::current().thr_fd.get().write(Some(new_pid_fd));
- THIS_PID
- .get()
- .write(Some(syscall::getpid().unwrap().try_into().unwrap()).unwrap());
+#[repr(C)] // TODO: is repr(C) required?
+pub(crate) struct StaticProcInfo {
+ pid: u32,
+ proc_fd: MaybeUninit,
+ has_proc_fd: bool,
+}
+struct DynamicProcInfo {
+ pgid: u32,
+ euid: u32,
+ suid: u32,
+ ruid: u32,
+ egid: u32,
+ rgid: u32,
+ sgid: u32,
+}
+
+static DYNAMIC_PROC_INFO: Mutex = Mutex::new(DynamicProcInfo {
+ pgid: u32::MAX,
+ ruid: u32::MAX,
+ euid: u32::MAX,
+ suid: u32::MAX,
+ rgid: u32::MAX,
+ egid: u32::MAX,
+ sgid: u32::MAX,
+});
+
+#[inline]
+pub(crate) fn static_proc_info() -> &'static StaticProcInfo {
+ unsafe { &*STATIC_PROC_INFO.get() }
+}
+#[inline]
+pub fn current_proc_fd() -> &'static FdGuard {
+ let info = static_proc_info();
+ assert!(info.has_proc_fd);
+ unsafe { info.proc_fd.assume_init_ref() }
+}
+
+struct ChildHookCommonArgs {
+ new_thr_fd: FdGuard,
+ new_proc_fd: Option,
+}
+
+unsafe fn child_hook_common(args: ChildHookCommonArgs) {
+ // TODO: just pass PID to child rather than obtaining it via IPC?
+ #[cfg(feature = "proc")]
+ let metadata = read_proc_meta(
+ args.new_proc_fd
+ .as_ref()
+ .expect("must be present with proc feature"),
+ )
+ .unwrap();
+
+ #[cfg(not(feature = "proc"))]
+ let metadata = ProcMeta::default();
+
+ if let Some(proc_fd) = &args.new_proc_fd {
+ crate::arch::PROC_FD.get().write(**proc_fd);
+ }
+
+ let old_proc_fd = STATIC_PROC_INFO
+ .get()
+ .replace(StaticProcInfo {
+ pid: metadata.pid,
+ has_proc_fd: args.new_proc_fd.is_some(),
+ proc_fd: args
+ .new_proc_fd
+ .map_or_else(MaybeUninit::uninit, MaybeUninit::new),
+ })
+ .proc_fd;
+ drop(old_proc_fd);
+
+ let old_thr_fd = RtTcb::current().thr_fd.get().replace(Some(args.new_thr_fd));
+ drop(old_thr_fd);
}
diff --git a/redox-rt/src/proc.rs b/redox-rt/src/proc.rs
index a6a4d67e60..ba8b290b42 100644
--- a/redox-rt/src/proc.rs
+++ b/redox-rt/src/proc.rs
@@ -1,6 +1,17 @@
-use core::{fmt::Debug, mem::size_of};
+use core::{
+ cell::SyncUnsafeCell,
+ fmt::Debug,
+ mem::{size_of, MaybeUninit},
+};
-use crate::{arch::*, auxv_defs::*};
+use crate::{
+ arch::*,
+ auxv_defs::*,
+ protocol::{ProcCall, ThreadCall},
+ read_proc_meta, static_proc_info,
+ sys::{proc_call, thread_call},
+ RtTcb, StaticProcInfo, DYNAMIC_PROC_INFO,
+};
use alloc::{boxed::Box, collections::BTreeMap, vec};
@@ -19,8 +30,8 @@ use goblin::elf64::{
use syscall::{
error::*,
flag::{MapFlags, SEEK_SET},
- GrantDesc, GrantFlags, Map, SetSighandlerData, MAP_FIXED_NOREPLACE, MAP_SHARED, O_CLOEXEC,
- PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE,
+ CallFlags, GrantDesc, GrantFlags, Map, ProcSchemeAttrs, SetSighandlerData, MAP_FIXED_NOREPLACE,
+ MAP_SHARED, O_CLOEXEC, PAGE_SIZE, PROT_EXEC, PROT_READ, PROT_WRITE,
};
pub enum FexecResult {
@@ -30,7 +41,6 @@ pub enum FexecResult {
Interp {
path: Box<[u8]>,
image_file: FdGuard,
- open_via_dup: FdGuard,
interp_override: InterpOverride,
},
}
@@ -53,11 +63,16 @@ pub struct ExtraInfo<'a> {
pub sigprocmask: u64,
/// File mode creation mask (POSIX)
pub umask: u32,
+ /// Thread handle
+ pub thr_fd: usize,
+ /// Process handle
+ pub proc_fd: usize,
}
pub fn fexec_impl(
image_file: FdGuard,
- open_via_dup: FdGuard,
+ thread_fd: &FdGuard,
+ proc_fd: &FdGuard,
memory_scheme_fd: &FdGuard,
path: &[u8],
args: A,
@@ -74,15 +89,14 @@ where
{
// Here, we do the minimum part of loading an application, which is what the kernel used to do.
// We load the executable into memory (albeit at different offsets in this executable), fix
- // some misalignments, and then execute the SYS_EXEC syscall to replace the program memory
- // entirely.
+ // some misalignments, and then switch address space.
let mut header_bytes = [0_u8; size_of::()];
pread_all(*image_file, 0, &mut header_bytes)?;
let header = Header::from_bytes(&header_bytes);
let grants_fd = {
- let current_addrspace_fd = FdGuard::new(syscall::dup(*open_via_dup, b"addrspace")?);
+ let current_addrspace_fd = FdGuard::new(syscall::dup(**thread_fd, b"addrspace")?);
FdGuard::new(syscall::dup(*current_addrspace_fd, b"empty")?)
};
@@ -107,7 +121,7 @@ where
|o| core::mem::take(&mut o.tree),
);
- pread_all(*image_file as usize, header.e_phoff.into(), phs).map_err(|_| Error::new(EIO))?;
+ pread_all(*image_file as usize, u64::from(header.e_phoff), phs).map_err(|_| Error::new(EIO))?;
for ph_idx in 0..phnum {
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
@@ -130,12 +144,15 @@ where
// PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice.
PT_INTERP => {
let mut interp = vec![0_u8; segment.p_filesz as usize];
- pread_all(*image_file as usize, segment.p_offset.into(), &mut interp)?;
+ pread_all(
+ *image_file as usize,
+ u64::from(segment.p_offset),
+ &mut interp,
+ )?;
return Ok(FexecResult::Interp {
path: interp.into_boxed_slice(),
image_file,
- open_via_dup,
interp_override: InterpOverride {
at_entry: header.e_entry as usize,
at_phnum: phnum,
@@ -180,7 +197,7 @@ where
};
pread_all(
*image_file,
- segment.p_offset.into(),
+ u64::from(segment.p_offset),
&mut dst_memory[voff..voff + filesz],
)?;
}
@@ -372,7 +389,12 @@ where
push(AT_REDOX_INHERITED_SIGPROCMASK)?;
push(extrainfo.umask as usize)?;
- push(AT_REDOX_UMASK);
+ push(AT_REDOX_UMASK)?;
+
+ push(extrainfo.thr_fd as usize)?;
+ push(AT_REDOX_THR_FD)?;
+ push(extrainfo.proc_fd as usize)?;
+ push(AT_REDOX_PROC_FD)?;
push(0)?;
@@ -390,7 +412,7 @@ where
push(argc)?;
- if let Ok(sighandler_fd) = syscall::dup(*open_via_dup, b"sighandler").map(FdGuard::new) {
+ if let Ok(sighandler_fd) = syscall::dup(**thread_fd, b"sighandler").map(FdGuard::new) {
let _ = syscall::write(
*sighandler_fd,
&SetSighandlerData {
@@ -400,15 +422,27 @@ where
proc_control_addr: 0,
},
);
+ // TODO: sync with procmgr
}
unsafe {
- deactivate_tcb(*open_via_dup)?;
+ deactivate_tcb(**thread_fd)?;
}
// TODO: Restore old name if exec failed?
- if let Ok(name_fd) = syscall::dup(*open_via_dup, b"name").map(FdGuard::new) {
- let _ = syscall::write(*name_fd, interp_override.as_ref().map_or(path, |o| &o.name));
+ {
+ let mut buf = [0; 32];
+ let new_name = interp_override.as_ref().map_or(path, |o| &o.name);
+ let len = new_name.len().min(32);
+ buf[..len].copy_from_slice(&new_name[..len]);
+ // XXX: takes &mut [] since it can mutate, but we could unsafe{}ly pass it directly
+ // otherwise
+ let _ = proc_call(
+ **proc_fd,
+ &mut buf,
+ CallFlags::empty(),
+ &[ProcCall::Rename as u64],
+ );
}
if interp_override.is_some() {
let mmap_min_fd = FdGuard::new(syscall::dup(*grants_fd, b"mmap-min-addr")?);
@@ -417,7 +451,7 @@ where
let _ = syscall::write(*mmap_min_fd, &usize::to_ne_bytes(aligned_last_addr));
}
- let addrspace_selection_fd = FdGuard::new(syscall::dup(*open_via_dup, b"current-addrspace")?);
+ let addrspace_selection_fd = FdGuard::new(syscall::dup(**thread_fd, b"current-addrspace")?);
let _ = syscall::write(
*addrspace_selection_fd,
@@ -631,32 +665,35 @@ impl Drop for MmapGuard {
}
}
+#[repr(transparent)]
pub struct FdGuard {
fd: usize,
- taken: bool,
}
impl FdGuard {
- pub fn new(fd: usize) -> Self {
- Self { fd, taken: false }
+ #[inline]
+ pub const fn new(fd: usize) -> Self {
+ Self { fd }
}
- pub fn take(&mut self) -> usize {
- self.taken = true;
- self.fd
+ #[inline]
+ pub fn take(self) -> usize {
+ let fd = self.fd;
+ core::mem::forget(self);
+ fd
}
}
impl core::ops::Deref for FdGuard {
type Target = usize;
+ #[inline]
fn deref(&self) -> &Self::Target {
&self.fd
}
}
impl Drop for FdGuard {
+ #[inline]
fn drop(&mut self) {
- if !self.taken {
- let _ = syscall::close(self.fd);
- }
+ let _ = syscall::close(self.fd);
}
}
impl Debug for FdGuard {
@@ -680,9 +717,13 @@ pub fn create_set_addr_space_buf(
/// 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 {
+pub fn fork_impl(args: &ForkArgs<'_>) -> Result {
let old_mask = crate::signal::get_sigmask()?;
- let pid = unsafe { Error::demux(__relibc_internal_fork_wrapper())? };
+ let pid = unsafe {
+ Error::demux(__relibc_internal_fork_wrapper(
+ args as *const ForkArgs as usize,
+ ))?
+ };
if pid == 0 {
crate::signal::set_sigmask(Some(old_mask), None)?;
@@ -690,36 +731,52 @@ pub fn fork_impl() -> Result {
Ok(pid)
}
-pub fn fork_inner(initial_rsp: *mut usize) -> Result {
- let (cur_filetable_fd, new_pid_fd, new_pid);
+pub enum ForkArgs<'a> {
+ Init {
+ this_thr_fd: &'a FdGuard,
+ auth: &'a FdGuard,
+ },
+ Managed,
+}
+
+pub fn fork_inner(initial_rsp: *mut usize, args: &ForkArgs) -> Result {
+ let (cur_filetable_fd, new_proc_fd, new_thr_fd, new_pid);
{
- let cur_pid_fd = FdGuard::new(syscall::open(
- "/scheme/thisproc/current/open_via_dup",
- O_CLOEXEC,
- )?);
- (new_pid_fd, new_pid) = new_child_process()?;
-
- copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
+ let cur_thr_fd = match args {
+ ForkArgs::Init { this_thr_fd, .. } => this_thr_fd,
+ ForkArgs::Managed => RtTcb::current().thread_fd(),
+ };
+ let NewChildProc {
+ proc_fd,
+ thr_fd,
+ pid,
+ } = new_child_process(args)?;
+ new_proc_fd = proc_fd;
+ new_thr_fd = thr_fd;
+ new_pid = pid;
// Copy existing files into new file table, but do not reuse the same file table (i.e. new
// parent FDs will not show up for the child).
{
- cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
+ cur_filetable_fd = FdGuard::new(syscall::dup(**cur_thr_fd, b"filetable")?);
// This must be done before the address space is copied.
unsafe {
+ let proc_fd = new_proc_fd.as_ref().map_or(usize::MAX, |p| **p);
+ //let _ = syscall::write(1, alloc::format!("FDTBL{}PROC{}THR{}\n", *cur_filetable_fd, proc_fd, *new_thr_fd).as_bytes());
initial_rsp.write(*cur_filetable_fd);
- initial_rsp.add(1).write(*new_pid_fd);
+ initial_rsp.add(1).write(proc_fd);
+ initial_rsp.add(2).write(*new_thr_fd);
}
}
// CoW-duplicate address space.
{
let new_addr_space_sel_fd =
- FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
+ FdGuard::new(syscall::dup(*new_thr_fd, b"current-addrspace")?);
- let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
+ let cur_addr_space_fd = FdGuard::new(syscall::dup(**cur_thr_fd, b"addrspace")?);
let new_addr_space_fd = FdGuard::new(syscall::dup(*cur_addr_space_fd, b"exclusive")?);
let mut grant_desc_buf = [GrantDesc::default(); 16];
@@ -795,14 +852,28 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result {
// 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 new_sighandler_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"sighandler")?);
let _ = syscall::write(
*new_sighandler_fd,
&crate::signal::current_setsighandler_struct(),
)?;
}
+ if let Some(ref proc_fd) = new_proc_fd {
+ proc_call(
+ **proc_fd,
+ &mut [],
+ CallFlags::empty(),
+ &[ProcCall::SyncSigPctl as u64],
+ )?;
+ thread_call(
+ *new_thr_fd,
+ &mut [],
+ CallFlags::empty(),
+ &[ThreadCall::SyncSigTctl as u64],
+ )?;
+ }
}
- copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
+ copy_env_regs(**cur_thr_fd, *new_thr_fd)?;
}
// Copy the file table. We do this last to ensure that all previously used file descriptors are
// closed. The only exception -- the filetable selection fd and the current filetable fd --
@@ -811,59 +882,115 @@ pub fn fork_inner(initial_rsp: *mut usize) -> Result {
// TODO: Use file descriptor forwarding or something similar to avoid copying the file
// table in the kernel.
let new_filetable_fd = FdGuard::new(syscall::dup(*cur_filetable_fd, b"copy")?);
- let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
+ let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"current-filetable")?);
let _ = syscall::write(
*new_filetable_sel_fd,
&usize::to_ne_bytes(*new_filetable_fd),
)?;
}
- let start_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"start")?);
+ let start_fd = FdGuard::new(syscall::dup(*new_thr_fd, b"start")?);
let _ = syscall::write(*start_fd, &[0])?;
Ok(new_pid)
}
-pub fn new_child_process() -> Result<(FdGuard, usize)> {
- // Create a new context (fields such as uid/gid will be inherited from the current context).
- let fd = FdGuard::new(syscall::open(
- "/scheme/thisproc/new/open_via_dup",
- O_CLOEXEC,
- )?);
+struct NewChildProc {
+ proc_fd: Option,
- // Extract pid.
- let mut buffer = [0_u8; 64];
- let len = syscall::fpath(*fd, &mut buffer)?;
- let buffer = buffer.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
-
- let colon_idx = buffer
- .iter()
- .position(|c| *c == b':')
- .ok_or(Error::new(EINVAL))?;
- let slash_idx = buffer
- .iter()
- .skip(colon_idx)
- .position(|c| *c == b'/')
- .ok_or(Error::new(EINVAL))?
- + colon_idx;
- let pid_bytes = buffer
- .get(colon_idx + 1..slash_idx)
- .ok_or(Error::new(EINVAL))?;
- let pid_str = core::str::from_utf8(pid_bytes).map_err(|_| Error::new(EINVAL))?;
- let pid = pid_str.parse::().map_err(|_| Error::new(EINVAL))?;
-
- Ok((fd, pid))
+ thr_fd: FdGuard,
+ pid: usize,
}
-pub fn copy_str(cur_pid_fd: usize, new_pid_fd: usize, key: &str) -> Result<()> {
- let cur_name_fd = FdGuard::new(syscall::dup(cur_pid_fd, key.as_bytes())?);
- let new_name_fd = FdGuard::new(syscall::dup(new_pid_fd, key.as_bytes())?);
+pub fn new_child_process(args: &ForkArgs<'_>) -> Result {
+ match *args {
+ ForkArgs::Managed => {
+ let proc_info = crate::static_proc_info();
+ assert!(
+ proc_info.has_proc_fd,
+ "cannot use ForkArgs::Managed without an existing proc info"
+ );
+ let this_proc_fd = unsafe { proc_info.proc_fd.assume_init_ref() };
+ let child_proc_fd = FdGuard::new(syscall::dup(**this_proc_fd, b"fork")?);
+ let only_thread_fd = FdGuard::new(syscall::dup(*child_proc_fd, b"thread-0")?);
+ let meta = read_proc_meta(&child_proc_fd)?;
+ Ok(NewChildProc {
+ proc_fd: Some(child_proc_fd),
+ thr_fd: only_thread_fd,
+ pid: meta.pid as usize,
+ })
+ }
+ #[cfg(feature = "proc")]
+ ForkArgs::Init { .. } => unreachable!(),
- // TODO: Max path size?
- let mut buf = [0_u8; 256];
- let len = syscall::read(*cur_name_fd, &mut buf)?;
- let buf = buf.get(..len).ok_or(Error::new(ENAMETOOLONG))?;
-
- syscall::write(*new_name_fd, &buf)?;
-
- Ok(())
+ #[cfg(not(feature = "proc"))]
+ ForkArgs::Init { this_thr_fd, auth } => {
+ let thr_fd = FdGuard::new(syscall::dup(**auth, b"new-context")?);
+ let buf = ProcSchemeAttrs {
+ pid: 0,
+ euid: 0,
+ egid: 0,
+ ens: 1,
+ debug_name: {
+ let mut buf = [0; 32];
+ let src = b"[init]";
+ buf[..src.len()].copy_from_slice(src);
+ buf
+ },
+ };
+ let attr_fd = FdGuard::new(syscall::dup(
+ *thr_fd,
+ alloc::format!("auth-{}-attrs", **auth).as_bytes(),
+ )?);
+ let _ = syscall::write(*attr_fd, &buf)?;
+ Ok(NewChildProc {
+ thr_fd,
+ pid: 1, // dummy fd to distinguish child from parent
+ proc_fd: None,
+ })
+ }
+ }
}
+
+pub unsafe fn make_init() -> [&'static FdGuard; 2] {
+ let proc_fd = FdGuard::new(
+ syscall::open("/scheme/proc/init", syscall::O_CLOEXEC).expect("failed to create init"),
+ );
+ syscall::sendfd(
+ *proc_fd,
+ syscall::dup(**RtTcb::current().thread_fd(), &[]).unwrap(),
+ 0,
+ 0,
+ )
+ .expect("failed to assign current thread to init process");
+
+ let managed_thr_fd = FdGuard::new(
+ syscall::dup(*proc_fd, b"thread-0").expect("failed to get managed thread for init"),
+ );
+
+ let managed_thr_fd = (*RtTcb::current().thr_fd.get()).insert(managed_thr_fd);
+
+ STATIC_PROC_INFO.get().write(crate::StaticProcInfo {
+ pid: 1,
+ proc_fd: MaybeUninit::new(proc_fd),
+ has_proc_fd: true,
+ });
+ *DYNAMIC_PROC_INFO.lock() = crate::DynamicProcInfo {
+ pgid: 1,
+ ruid: 0,
+ euid: 0,
+ suid: 0,
+ rgid: 0,
+ egid: 0,
+ sgid: 0,
+ };
+ [
+ (*STATIC_PROC_INFO.get()).proc_fd.assume_init_ref(),
+ managed_thr_fd,
+ ]
+}
+pub(crate) static STATIC_PROC_INFO: SyncUnsafeCell =
+ SyncUnsafeCell::new(StaticProcInfo {
+ pid: 0,
+ proc_fd: MaybeUninit::zeroed(),
+ has_proc_fd: false,
+ });
diff --git a/redox-rt/src/protocol.rs b/redox-rt/src/protocol.rs
new file mode 100644
index 0000000000..75fe33f8ac
--- /dev/null
+++ b/redox-rt/src/protocol.rs
@@ -0,0 +1,196 @@
+use bitflags::bitflags;
+
+#[derive(Clone, Copy, Debug, Default)]
+#[repr(C)]
+pub struct ProcMeta {
+ pub pid: u32,
+ pub pgid: u32,
+ pub ppid: u32,
+ pub ruid: u32,
+ pub euid: u32,
+ pub suid: u32,
+ pub rgid: u32,
+ pub egid: u32,
+ pub sgid: u32,
+ pub ens: u32,
+ pub rns: u32,
+}
+unsafe impl plain::Plain for ProcMeta {}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[repr(usize)]
+pub enum ProcCall {
+ Waitpid = 0,
+ Setrens = 1,
+ Exit = 2,
+ Waitpgid = 3,
+ SetResugid = 4,
+ Setpgid = 5,
+ Getsid = 6,
+ Setsid = 7,
+ Kill = 8,
+ Sigq = 9,
+
+ // TODO: replace with sendfd equivalent syscall for sending memory
+ SyncSigPctl = 10,
+ Sigdeq = 11,
+ Getppid = 12,
+ Rename = 13,
+}
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[repr(usize)]
+pub enum ThreadCall {
+ // TODO: replace with sendfd equivalent syscall for sending memory, or force userspace to
+ // obtain its TCB memory from this server
+ SyncSigTctl = 0,
+ SignalThread = 1,
+}
+
+impl ProcCall {
+ pub fn try_from_raw(raw: usize) -> Option {
+ Some(match raw {
+ 0 => Self::Waitpid,
+ 1 => Self::Setrens,
+ 2 => Self::Exit,
+ 3 => Self::Waitpgid,
+ 4 => Self::SetResugid,
+ 5 => Self::Setpgid,
+ 6 => Self::Getsid,
+ 7 => Self::Setsid,
+ 8 => Self::Kill,
+ 9 => Self::Sigq,
+ 10 => Self::SyncSigPctl,
+ 11 => Self::Sigdeq,
+ 12 => Self::Getppid,
+ 13 => Self::Rename,
+ _ => return None,
+ })
+ }
+}
+impl ThreadCall {
+ pub fn try_from_raw(raw: usize) -> Option {
+ Some(match raw {
+ 0 => Self::SyncSigTctl,
+ 1 => Self::SignalThread,
+ _ => return None,
+ })
+ }
+}
+
+bitflags! {
+ #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
+ pub struct WaitFlags: usize {
+ const WNOHANG = 0x01;
+ const WUNTRACED = 0x02;
+ const WCONTINUED = 0x08;
+ }
+}
+/// True if status indicates the child is stopped.
+pub fn wifstopped(status: usize) -> bool {
+ (status & 0xff) == 0x7f
+}
+
+/// If wifstopped(status), the signal that stopped the child.
+pub fn wstopsig(status: usize) -> usize {
+ (status >> 8) & 0xff
+}
+
+/// True if status indicates the child continued after a stop.
+pub fn wifcontinued(status: usize) -> bool {
+ status == 0xffff
+}
+
+/// True if STATUS indicates termination by a signal.
+pub fn wifsignaled(status: usize) -> bool {
+ ((status & 0x7f) + 1) as i8 >= 2
+}
+
+/// If wifsignaled(status), the terminating signal.
+pub fn wtermsig(status: usize) -> usize {
+ status & 0x7f
+}
+
+/// True if status indicates normal termination.
+pub fn wifexited(status: usize) -> bool {
+ wtermsig(status) == 0
+}
+
+/// If wifexited(status), the exit status.
+pub fn wexitstatus(status: usize) -> usize {
+ (status >> 8) & 0xff
+}
+
+/// True if status indicates a core dump was created.
+pub fn wcoredump(status: usize) -> bool {
+ (status & 0x80) != 0
+}
+#[derive(Clone, Copy, Debug)]
+pub enum ProcKillTarget {
+ ThisGroup,
+ SingleProc(usize),
+ ProcGroup(usize),
+ All,
+}
+impl ProcKillTarget {
+ pub fn raw(self) -> usize {
+ match self {
+ Self::ThisGroup => 0,
+ Self::SingleProc(p) => p,
+ Self::ProcGroup(g) => usize::wrapping_neg(g),
+ Self::All => usize::wrapping_neg(1),
+ }
+ }
+ pub fn from_raw(raw: usize) -> Self {
+ let raw = raw as isize;
+ if raw == 0 {
+ Self::ThisGroup
+ } else if raw == -1 {
+ Self::All
+ } else if raw < 0 {
+ Self::ProcGroup(raw as usize)
+ } else {
+ Self::SingleProc(raw as usize)
+ }
+ }
+}
+#[derive(Copy, Clone, Debug, Default, PartialEq)]
+#[repr(C)]
+pub struct RtSigInfo {
+ pub arg: usize,
+ pub code: i32,
+ pub uid: u32,
+ pub pid: u32, // TODO: usize?
+}
+unsafe impl plain::Plain for RtSigInfo {}
+
+pub const SIGHUP: usize = 1;
+pub const SIGINT: usize = 2;
+pub const SIGQUIT: usize = 3;
+pub const SIGILL: usize = 4;
+pub const SIGTRAP: usize = 5;
+pub const SIGABRT: usize = 6;
+pub const SIGBUS: usize = 7;
+pub const SIGFPE: usize = 8;
+pub const SIGKILL: usize = 9;
+pub const SIGUSR1: usize = 10;
+pub const SIGSEGV: usize = 11;
+pub const SIGUSR2: usize = 12;
+pub const SIGPIPE: usize = 13;
+pub const SIGALRM: usize = 14;
+pub const SIGTERM: usize = 15;
+pub const SIGSTKFLT: usize = 16;
+pub const SIGCHLD: usize = syscall::SIGCHLD;
+pub const SIGCONT: usize = 18;
+pub const SIGSTOP: usize = 19;
+pub const SIGTSTP: usize = syscall::SIGTSTP;
+pub const SIGTTIN: usize = syscall::SIGTTIN;
+pub const SIGTTOU: usize = syscall::SIGTTOU;
+pub const SIGURG: usize = 23;
+pub const SIGXCPU: usize = 24;
+pub const SIGXFSZ: usize = 25;
+pub const SIGVTALRM: usize = 26;
+pub const SIGPROF: usize = 27;
+pub const SIGWINCH: usize = 28;
+pub const SIGIO: usize = 29;
+pub const SIGPWR: usize = 30;
+pub const SIGSYS: usize = 31;
diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs
index f303383957..5810398232 100644
--- a/redox-rt/src/signal.rs
+++ b/redox-rt/src/signal.rs
@@ -1,12 +1,23 @@
-use core::{ffi::c_int, mem::MaybeUninit, ptr::NonNull, sync::atomic::Ordering};
+use core::{ffi::c_int, ptr::NonNull, sync::atomic::Ordering};
use syscall::{
- data::AtomicU64, Error, RawAction, Result, RtSigInfo, SenderInfo, SetSighandlerData,
+ data::AtomicU64, CallFlags, Error, RawAction, Result, SenderInfo, SetSighandlerData,
SigProcControl, Sigcontrol, SigcontrolFlags, TimeSpec, EAGAIN, EINTR, EINVAL, ENOMEM, EPERM,
- SIGCHLD, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGWINCH,
};
-use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb};
+use crate::{
+ arch::*,
+ current_proc_fd,
+ proc::FdGuard,
+ protocol::{
+ ProcCall, RtSigInfo, ThreadCall, SIGCHLD, SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU,
+ SIGURG, SIGWINCH,
+ },
+ static_proc_info,
+ sync::Mutex,
+ sys::{proc_call, this_thread_call},
+ RtTcb, Tcb,
+};
#[cfg(target_arch = "x86_64")]
static CPUID_EAX1_ECX: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
@@ -149,11 +160,18 @@ unsafe fn inner(stack: &mut SigStack) {
let handler = match sigaction.kind {
SigactionKind::Ignore => {
- panic!("ctl {:x?} signal {}", os.control, stack.sig_num)
+ panic!("ctl {:#x?} signal {}", os.control, stack.sig_num)
}
SigactionKind::Default => {
- syscall::exit(stack.sig_num as usize);
- unreachable!();
+ let sig = (stack.sig_num & 0x3f) as u8;
+
+ let _ = proc_call(
+ **current_proc_fd(),
+ &mut [],
+ CallFlags::empty(),
+ &[ProcCall::Exit as u64, u64::from(sig) << 8],
+ );
+ core::intrinsics::abort()
}
SigactionKind::Handled { handler } => handler,
};
@@ -270,7 +288,10 @@ fn get_allowset_raw(words: &[AtomicU64; 2]) -> u64 {
(words[0].load(Ordering::Relaxed) >> 32) | ((words[1].load(Ordering::Relaxed) >> 32) << 32)
}
/// Sets mask from old to new, returning what was pending at the time.
-fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 {
+fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new_raw: u64) -> u64 {
+ // TODO: should these bits always be set, or never be set?
+ let new = new_raw | ALLOWSET_ALWAYS;
+
// This assumes *only this thread* can change the allowset. If this rule is broken, the use of
// fetch_add will corrupt the words entirely. fetch_add is very efficient on x86, being
// generated as LOCK XADD which is the fastest RMW instruction AFAIK.
@@ -285,6 +306,7 @@ fn set_allowset_raw(words: &[AtomicU64; 2], old: u64, new: u64) -> u64 {
prev_w0 | (prev_w1 << 32)
}
+const ALLOWSET_ALWAYS: u64 = sig_bit(SIGSTOP as u32) | sig_bit(SIGKILL as u32);
fn modify_sigmask(old: Option<&mut u64>, op: Option u64>) -> Result<()> {
let _guard = tmp_disable_signals();
let ctl = current_sigctl();
@@ -375,9 +397,29 @@ fn convert_old(action: &RawAction) -> Sigaction {
}
pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction>) -> Result<()> {
- if matches!(usize::from(signal), 0 | 32 | SIGKILL | SIGSTOP | 65..) {
+ // TODO: Now that the goal of keeping logic out of the IPC backend, no longer holds when
+ // procmgr has taken over signal handling from the kernel, it would probably make sense to make
+ // parts of this function an IPC call, for synchronization purposes. Apart from SA_RESETHAND
+ // logic which may need to be fast, regular sigaction is typically in the 'configuration'
+ // category, allowed to be slower.
+
+ if matches!(usize::from(signal), 0 | 32 | 65..) {
return Err(Error::new(EINVAL));
}
+ if matches!(usize::from(signal), SIGKILL | SIGSTOP) {
+ if new.is_some() {
+ return Err(Error::new(EINVAL));
+ }
+ if let Some(old) = old {
+ // TODO: Is this the correct value to set it to?
+ *old = Sigaction {
+ kind: SigactionKind::Default,
+ mask: 0,
+ flags: SigactionFlags::empty(),
+ };
+ }
+ return Ok(());
+ }
let _sigguard = tmp_disable_signals();
let ctl = current_sigctl();
@@ -398,8 +440,16 @@ pub fn sigaction(signal: u8, new: Option<&Sigaction>, old: Option<&mut Sigaction
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.
+ let sig_group = (signal - 1) / 32;
+ let sig_idx = (signal - 1) % 32;
+
+ // TODO: relibc and the procmgr has access to all threads, redox_rt doesn't currently.
+ // Do this for all threads!
+ ctl.word[usize::from(sig_group)].fetch_and(!(1 << sig_idx), Ordering::Relaxed);
+ PROC_CONTROL_STRUCT
+ .pending
+ .fetch_and(!sig_bit(signal.into()), Ordering::Relaxed);
+
// TODO: handle tmp_disable_signals
(
MASK_DONTCARE,
@@ -497,7 +547,7 @@ bitflags::bitflags! {
const STORED_FLAGS: u32 = 0xfe00_0000;
fn default_handler(sig: c_int) {
- syscall::exit(sig as usize);
+ unreachable!();
}
#[derive(Clone, Copy)]
@@ -529,8 +579,8 @@ const fn sig_bit(sig: u32) -> u64 {
1 << (sig - 1)
}
-pub fn setup_sighandler(tcb: &RtTcb) {
- {
+pub fn setup_sighandler(tcb: &RtTcb, first_thread: bool) {
+ if first_thread {
let _guard = SIGACTIONS_LOCK.lock();
for (sig_idx, action) in PROC_CONTROL_STRUCT.actions.iter().enumerate() {
let sig = sig_idx + 1;
@@ -554,8 +604,7 @@ pub fn setup_sighandler(tcb: &RtTcb) {
// equivalent to not using any altstack at all (the default).
arch.altstack_top = usize::MAX;
arch.altstack_bottom = 0;
- // TODO
- #[cfg(any(target_arch = "x86", target_arch = "aarch64", target_arch = "riscv64"))]
+ #[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
{
arch.pctl = core::ptr::addr_of!(PROC_CONTROL_STRUCT) as usize;
}
@@ -574,6 +623,12 @@ pub fn setup_sighandler(tcb: &RtTcb) {
syscall::dup(**tcb.thread_fd(), b"sighandler").expect("failed to open sighandler fd"),
);
let _ = syscall::write(*fd, &data).expect("failed to write to sighandler fd");
+ this_thread_call(
+ &mut [],
+ CallFlags::empty(),
+ &[ThreadCall::SyncSigTctl as u64],
+ )
+ .expect("failed to sync signal tctl");
// TODO: Inherited set of ignored signals
set_sigmask(Some(0), None);
@@ -684,17 +739,25 @@ pub fn await_signal_async(inner_allowset: u64) -> Result {
},
&mut TimeSpec::default(),
);
- set_allowset_raw(&control.word, inner_allowset, old_allowset);
if res == Err(Error::new(EINTR)) {
unsafe {
manually_enter_trampoline();
}
}
+ // POSIX says it shall restore the mask to what it was prior to the call, which is interpreted
+ // as allowing any changes to sigprocmask inside the signal handler, to be discarded.
+ set_allowset_raw(&control.word, inner_allowset, old_allowset);
res?;
unreachable!()
}
+/*#[no_mangle]
+pub extern "C" fn __redox_rt_debug_sigctl() {
+ let tcb = &RtTcb::current().control;
+ let _ = syscall::write(1, alloc::format!("SIGCTL: {tcb:#x?}\n").as_bytes());
+}*/
+
// TODO: deadline-based API
pub fn await_signal_sync(inner_allowset: u64, timeout: Option<&TimeSpec>) -> Result {
let _guard = tmp_disable_signals();
@@ -764,15 +827,17 @@ fn try_claim_single(sig_idx: u32, thread_control: Option<&Sigcontrol>) -> Option
if sig_group == 1 && thread_control.is_none() {
// Queued (realtime) signal
- let mut ret = MaybeUninit::::uninit();
- let rt_inf = unsafe {
- syscall::syscall2(
- syscall::SYS_SIGDEQUEUE,
- ret.as_mut_ptr() as usize,
- sig_idx as usize - 32,
+ let rt_inf: RtSigInfo = unsafe {
+ let mut buf = [0_u8; size_of::()];
+ buf[..4].copy_from_slice(&(sig_idx - 32).to_ne_bytes());
+ proc_call(
+ **static_proc_info().proc_fd.assume_init_ref(),
+ &mut buf,
+ CallFlags::empty(),
+ &[ProcCall::Sigdeq as u64],
)
.ok()?;
- ret.assume_init()
+ core::mem::transmute(buf)
};
Some(SiginfoAbi {
si_signo: sig_idx as i32 + 1,
diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs
index 2f9bd82c30..82ece48fdc 100644
--- a/redox-rt/src/sys.rs
+++ b/redox-rt/src/sys.rs
@@ -1,24 +1,31 @@
use core::{
+ mem::size_of,
ptr::addr_of,
sync::atomic::{AtomicU32, Ordering},
};
use syscall::{
- error::{Error, Result, EINTR},
- RtSigInfo, TimeSpec,
+ error::{self, Error, Result, EINTR},
+ CallFlags, TimeSpec, EINVAL, ERESTART,
};
-use crate::{arch::manually_enter_trampoline, proc::FdGuard, signal::tmp_disable_signals, Tcb};
+use crate::{
+ arch::manually_enter_trampoline,
+ protocol::{ProcCall, ProcKillTarget, RtSigInfo, ThreadCall, WaitFlags},
+ signal::tmp_disable_signals,
+ DynamicProcInfo, RtTcb, Tcb, DYNAMIC_PROC_INFO,
+};
#[inline]
-fn wrapper(restart: bool, mut f: impl FnMut() -> Result) -> Result {
+fn wrapper(restart: bool, erestart: bool, mut f: impl FnMut() -> Result) -> Result {
loop {
let _guard = tmp_disable_signals();
let rt_sigarea = unsafe { &Tcb::current().unwrap().os_specific };
let res = f();
+ let code = if erestart { ERESTART } else { EINTR };
if let Err(err) = res
- && err == Error::new(EINTR)
+ && err == Error::new(code)
{
unsafe {
manually_enter_trampoline();
@@ -35,49 +42,60 @@ fn wrapper(restart: bool, mut f: impl FnMut() -> Result) -> Result {
// TODO: uninitialized memory?
#[inline]
pub fn posix_read(fd: usize, buf: &mut [u8]) -> Result {
- wrapper(true, || syscall::read(fd, buf))
+ wrapper(true, false, || syscall::read(fd, buf))
}
#[inline]
pub fn posix_write(fd: usize, buf: &[u8]) -> Result {
- wrapper(true, || syscall::write(fd, buf))
+ wrapper(true, false, || syscall::write(fd, buf))
}
#[inline]
-pub fn posix_kill(pid: usize, sig: usize) -> Result<()> {
- match wrapper(false, || syscall::kill(pid, sig)) {
- Ok(_) | Err(Error { errno: EINTR }) => Ok(()),
+pub fn posix_kill(target: ProcKillTarget, sig: usize) -> Result<()> {
+ match wrapper(false, true, || {
+ this_proc_call(
+ &mut [],
+ CallFlags::empty(),
+ &[ProcCall::Kill as u64, target.raw() as u64, sig as u64],
+ )
+ }) {
+ Ok(_) | Err(Error { errno: ERESTART }) => Ok(()),
Err(error) => Err(error),
}
}
#[inline]
pub fn posix_sigqueue(pid: usize, sig: usize, arg: usize) -> Result<()> {
- let siginf = RtSigInfo {
+ let mut siginf = RtSigInfo {
arg,
code: -1, // TODO: SI_QUEUE constant
uid: 0, // TODO
pid: posix_getpid(),
};
- match wrapper(false, || unsafe {
- syscall::syscall3(syscall::SYS_SIGENQUEUE, pid, sig, addr_of!(siginf) as usize)
+ match wrapper(false, true, || {
+ this_proc_call(
+ unsafe { plain::as_mut_bytes(&mut siginf) },
+ CallFlags::empty(),
+ &[ProcCall::Sigq as u64, pid as u64, sig as u64],
+ )
}) {
- Ok(_) | Err(Error { errno: EINTR }) => Ok(()),
+ Ok(_)
+ | Err(Error {
+ errno: error::EINTR,
+ }) => Ok(()),
Err(error) => Err(error),
}
}
#[inline]
pub fn posix_getpid() -> u32 {
// SAFETY: read-only except during program/fork child initialization
- unsafe { crate::THIS_PID.get().read() }
+ unsafe { addr_of!((*crate::STATIC_PROC_INFO.get()).pid).read() }
}
#[inline]
-pub fn posix_killpg(pgrp: usize, sig: usize) -> Result<()> {
- match wrapper(false, || syscall::kill(usize::wrapping_neg(pgrp), sig)) {
- Ok(_) | Err(Error { errno: EINTR }) => Ok(()),
- Err(error) => Err(error),
- }
+pub fn posix_getppid() -> u32 {
+ this_proc_call(&mut [], CallFlags::empty(), &[ProcCall::Getppid as u64]).expect("cannot fail")
+ as u32
}
#[inline]
pub unsafe fn sys_futex_wait(addr: *mut u32, val: u32, deadline: Option<&TimeSpec>) -> Result<()> {
- wrapper(true, || {
+ wrapper(true, false, || {
syscall::syscall5(
syscall::SYS_FUTEX,
addr as usize,
@@ -101,19 +119,91 @@ pub unsafe fn sys_futex_wake(addr: *mut u32, num: u32) -> Result {
)
.map(|awoken| awoken as u32)
}
-pub fn sys_waitpid(pid: usize, status: &mut usize, flags: usize) -> Result {
- wrapper(true, || {
- syscall::waitpid(
- pid,
- status,
- syscall::WaitFlags::from_bits(flags).expect("waitpid: invalid bit pattern"),
+pub fn sys_call(
+ fd: usize,
+ payload: &mut [u8],
+ flags: CallFlags,
+ metadata: &[u64],
+) -> Result {
+ unsafe {
+ syscall::syscall5(
+ syscall::SYS_CALL,
+ fd,
+ payload.as_mut_ptr() as usize,
+ payload.len(),
+ metadata.len() | flags.bits(),
+ metadata.as_ptr() as usize,
+ )
+ }
+}
+pub fn this_proc_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result {
+ proc_call(**crate::current_proc_fd(), payload, flags, metadata)
+}
+pub fn proc_call(
+ proc_fd: usize,
+ payload: &mut [u8],
+ flags: CallFlags,
+ metadata: &[u64],
+) -> Result {
+ sys_call(proc_fd, payload, flags, metadata)
+}
+pub fn thread_call(
+ thread_fd: usize,
+ payload: &mut [u8],
+ flags: CallFlags,
+ metadata: &[u64],
+) -> Result {
+ sys_call(thread_fd, payload, flags, metadata)
+}
+pub fn this_thread_call(payload: &mut [u8], flags: CallFlags, metadata: &[u64]) -> Result {
+ thread_call(**RtTcb::current().thread_fd(), payload, flags, metadata)
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum WaitpidTarget {
+ AnyChild,
+ AnyGroupMember,
+ SingleProc { pid: usize },
+ ProcGroup { pgid: usize },
+}
+impl WaitpidTarget {
+ pub fn from_posix_arg(raw: isize) -> Self {
+ match raw {
+ 0 => Self::AnyGroupMember,
+ -1 => Self::AnyChild,
+ 1.. => Self::SingleProc { pid: raw as usize },
+ ..-1 => Self::ProcGroup {
+ pgid: -raw as usize,
+ },
+ }
+ }
+}
+
+pub fn sys_waitpid(target: WaitpidTarget, status: &mut usize, flags: WaitFlags) -> Result {
+ let (call, pid) = match target {
+ WaitpidTarget::AnyChild => (ProcCall::Waitpid, 0),
+ WaitpidTarget::SingleProc { pid } => (ProcCall::Waitpid, pid),
+ WaitpidTarget::AnyGroupMember => (ProcCall::Waitpgid, 0),
+ WaitpidTarget::ProcGroup { pgid } => (ProcCall::Waitpgid, pgid),
+ };
+ wrapper(true, false, || {
+ this_proc_call(
+ unsafe { plain::as_mut_bytes(status) },
+ CallFlags::empty(),
+ &[call as u64, pid as u64, flags.bits() as u64],
)
})
}
pub fn posix_kill_thread(thread_fd: usize, signal: u32) -> Result<()> {
- let killfd = FdGuard::new(syscall::dup(thread_fd, b"signal")?);
- match wrapper(false, || syscall::write(*killfd, &signal.to_ne_bytes())) {
- Ok(_) | Err(Error { errno: EINTR }) => Ok(()),
+ match wrapper(false, true, || {
+ thread_call(
+ thread_fd,
+ &mut [],
+ CallFlags::empty(),
+ &[ThreadCall::SignalThread as u64, signal.into()],
+ )
+ }) {
+ Ok(_) | Err(Error { errno: ERESTART }) => Ok(()),
Err(error) => Err(error),
}
}
@@ -134,3 +224,126 @@ pub fn swap_umask(mask: u32) -> u32 {
pub fn get_umask() -> u32 {
UMASK.load(Ordering::Acquire)
}
+
+/// Real/Effective/Set-User/Group ID
+pub struct Resugid {
+ pub ruid: T,
+ pub euid: T,
+ pub suid: T,
+ pub rgid: T,
+ pub egid: T,
+ pub sgid: T,
+}
+
+/// Sets [res][ug]id, fields that are None will be unchanged.
+pub fn posix_setresugid(ids: &Resugid