diff --git a/local/patches/relibc/P3-eventfd.patch b/local/patches/relibc/P3-eventfd.patch index f711ad5e..e68ea35e 100644 --- a/local/patches/relibc/P3-eventfd.patch +++ b/local/patches/relibc/P3-eventfd.patch @@ -1,19 +1,23 @@ -diff --git a/src/header/mod.rs b/src/header/mod.rs ---- a/src/header/mod.rs -+++ b/src/header/mod.rs -@@ -88,6 +88,7 @@ +diff -ruN a/src/header/mod.rs b/src/header/mod.rs +--- a/src/header/mod.rs 2026-04-15 09:40:30.417847129 +0100 ++++ b/src/header/mod.rs 2026-04-15 09:46:42.009254774 +0100 +@@ -89,6 +89,7 @@ + // TODO: stropts.h (deprecated) pub mod sys_auxv; pub mod sys_epoll; +pub mod sys_eventfd; pub mod sys_file; pub mod sys_ioctl; -diff --git a/src/header/sys_eventfd/cbindgen.toml b/src/header/sys_eventfd/cbindgen.toml -new file mode 100644 ---- /dev/null -+++ b/src/header/sys_eventfd/cbindgen.toml -@@ -0,0 +1,9 @@ + // TODO: sys/ipc.h +diff -ruN a/src/header/sys_eventfd/cbindgen.toml b/src/header/sys_eventfd/cbindgen.toml +--- a/src/header/sys_eventfd/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_eventfd/cbindgen.toml 2026-04-15 09:46:42.009280833 +0100 +@@ -0,0 +1,12 @@ +sys_includes = ["stdint.h"] +include_guard = "_SYS_EVENTFD_H" ++trailer = """ ++typedef uint64_t eventfd_t; ++""" +language = "C" +style = "Tag" +no_includes = true @@ -21,11 +25,10 @@ new file mode 100644 + +[enum] +prefix_with_name = true -diff --git a/src/header/sys_eventfd/mod.rs b/src/header/sys_eventfd/mod.rs -new file mode 100644 ---- /dev/null -+++ b/src/header/sys_eventfd/mod.rs -@@ -0,0 +1,89 @@ +diff -ruN a/src/header/sys_eventfd/mod.rs b/src/header/sys_eventfd/mod.rs +--- a/src/header/sys_eventfd/mod.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_eventfd/mod.rs 2026-04-15 09:46:42.009305629 +0100 +@@ -0,0 +1,90 @@ +//! `sys/eventfd.h` implementation. +//! +//! Non-POSIX, see . @@ -47,6 +50,7 @@ new file mode 100644 +pub const EFD_CLOEXEC: c_int = 0x80000; +pub const EFD_NONBLOCK: c_int = 0x800; +pub const EFD_SEMAPHORE: c_int = 0x1; ++pub type eventfd_t = u64; + +fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> { + match Sys::read(fd, buf)? { @@ -101,17 +105,17 @@ new file mode 100644 +} + +#[unsafe(no_mangle)] -+pub unsafe extern "C" fn eventfd_read(fd: c_int, value: *mut u64) -> c_int { ++pub unsafe extern "C" fn eventfd_read(fd: c_int, value: *mut eventfd_t) -> c_int { + if value.is_null() { + ERRNO.set(EFAULT); + return -1; + } -+ let buf = unsafe { slice::from_raw_parts_mut(value.cast::(), mem::size_of::()) }; ++ let buf = unsafe { slice::from_raw_parts_mut(value.cast::(), mem::size_of::()) }; + read_exact(fd, buf).map(|()| 0).or_minus_one_errno() +} + +#[unsafe(no_mangle)] -+pub unsafe extern "C" fn eventfd_write(fd: c_int, value: u64) -> c_int { -+ let buf = unsafe { slice::from_raw_parts((&raw const value).cast::(), mem::size_of::()) }; ++pub unsafe extern "C" fn eventfd_write(fd: c_int, value: eventfd_t) -> c_int { ++ let buf = unsafe { slice::from_raw_parts((&raw const value).cast::(), mem::size_of::()) }; + write_exact(fd, buf).map(|()| 0).or_minus_one_errno() +} diff --git a/local/patches/relibc/P3-ipc-tests.patch b/local/patches/relibc/P3-ipc-tests.patch new file mode 100644 index 00000000..a0c30cfb --- /dev/null +++ b/local/patches/relibc/P3-ipc-tests.patch @@ -0,0 +1,92 @@ +diff --git a/tests/semaphore/sem_open.c b/tests/semaphore/sem_open.c +new file mode 100644 +--- /dev/null ++++ b/tests/semaphore/sem_open.c +@@ -0,0 +1,15 @@ ++#include ++#include ++#include ++#include ++ ++int main(void) { ++ sem_t *sem = sem_open("/relibc-test-sem", O_CREAT | O_EXCL, 0600, 1); ++ assert(sem != SEM_FAILED); ++ assert(sem_wait(sem) == 0); ++ assert(sem_post(sem) == 0); ++ assert(sem_close(sem) == 0); ++ assert(sem_unlink("/relibc-test-sem") == 0); ++ puts("sem_open ok"); ++ return 0; ++} +diff --git a/tests/sys_shm/shmget.c b/tests/sys_shm/shmget.c +new file mode 100644 +--- /dev/null ++++ b/tests/sys_shm/shmget.c +@@ -0,0 +1,15 @@ ++#include ++#include ++#include ++#include ++ ++int main(void) { ++ int id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600); ++ assert(id >= 0); ++ void *ptr = shmat(id, 0, 0); ++ assert(ptr != (void *)-1); ++ assert(shmdt(ptr) == 0); ++ assert(shmctl(id, IPC_RMID, 0) == 0); ++ puts("shmget ok"); ++ return 0; ++} +diff --git a/tests/sys_timerfd/timerfd.c b/tests/sys_timerfd/timerfd.c +new file mode 100644 +--- /dev/null ++++ b/tests/sys_timerfd/timerfd.c +@@ -0,0 +1,18 @@ ++#include ++#include ++#include ++#include ++#include ++ ++int main(void) { ++ int fd = timerfd_create(CLOCK_REALTIME, 0); ++ if (fd < 0) { ++ puts("timerfd unavailable"); ++ return 0; ++ } ++ struct itimerspec spec; ++ memset(&spec, 0, sizeof(spec)); ++ spec.it_value.tv_nsec = 1; ++ assert(timerfd_settime(fd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL) == 0); ++ assert(close(fd) == 0); ++ puts("timerfd ok"); ++ return 0; ++} +diff --git a/tests/sys_signalfd/signalfd.c b/tests/sys_signalfd/signalfd.c +new file mode 100644 +--- /dev/null ++++ b/tests/sys_signalfd/signalfd.c +@@ -0,0 +1,20 @@ ++#include ++#include ++#include ++#include ++#include ++ ++int main(void) { ++ sigset_t mask; ++ assert(sigemptyset(&mask) == 0); ++ assert(sigaddset(&mask, SIGUSR1) == 0); ++ int fd = signalfd(-1, &mask, sizeof(mask)); ++ if (fd < 0) { ++ puts("signalfd unavailable"); ++ return 0; ++ } ++ assert(kill(getpid(), SIGUSR1) == 0); ++ struct signalfd_siginfo info; ++ assert(read(fd, &info, sizeof(info)) == (ssize_t)sizeof(info)); ++ assert(info.ssi_signo == SIGUSR1); ++ puts("signalfd ok"); ++ return 0; ++} diff --git a/local/patches/relibc/P3-signalfd-header.patch b/local/patches/relibc/P3-signalfd-header.patch new file mode 100644 index 00000000..e4fd0441 --- /dev/null +++ b/local/patches/relibc/P3-signalfd-header.patch @@ -0,0 +1,37 @@ +diff -ruN a/src/header/mod.rs b/src/header/mod.rs +--- a/src/header/mod.rs 2026-04-15 09:40:30.417847129 +0100 ++++ b/src/header/mod.rs 2026-04-15 09:48:02.257729647 +0100 +@@ -103,6 +103,7 @@ + pub mod sys_stat; + pub mod sys_statvfs; + pub mod sys_time; ++pub mod sys_signalfd; + #[deprecated] + pub mod sys_timeb; + //pub mod sys_times; +diff -ruN a/src/header/sys_signalfd/cbindgen.toml b/src/header/sys_signalfd/cbindgen.toml +--- a/src/header/sys_signalfd/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_signalfd/cbindgen.toml 2026-04-15 09:48:02.257754724 +0100 +@@ -0,0 +1,12 @@ ++sys_includes = ["signal.h", "stdint.h"] ++include_guard = "_SYS_SIGNALFD_H" ++language = "C" ++style = "Tag" ++no_includes = true ++cpp_compat = true ++ ++[enum] ++prefix_with_name = true ++ ++[export.rename] ++"signalfd_siginfo" = "struct signalfd_siginfo" +diff -ruN a/src/header/sys_signalfd/mod.rs b/src/header/sys_signalfd/mod.rs +--- a/src/header/sys_signalfd/mod.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_signalfd/mod.rs 2026-04-15 09:48:02.257778048 +0100 +@@ -0,0 +1,6 @@ ++//! `sys/signalfd.h` implementation. ++ ++pub use crate::header::signal::{SFD_CLOEXEC, SFD_NONBLOCK, signalfd, signalfd4, signalfd_siginfo}; ++ ++#[unsafe(no_mangle)] ++pub extern "C" fn _cbindgen_export_sys_signalfd_siginfo(siginfo: signalfd_siginfo) {} diff --git a/local/patches/relibc/P3-signalfd.patch b/local/patches/relibc/P3-signalfd.patch index 18be3ad1..af191231 100644 --- a/local/patches/relibc/P3-signalfd.patch +++ b/local/patches/relibc/P3-signalfd.patch @@ -1,7 +1,8 @@ -diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs ---- a/src/header/signal/mod.rs -+++ b/src/header/signal/mod.rs -@@ -33,6 +33,10 @@ +diff -ruN a/src/header/signal/mod.rs b/src/header/signal/mod.rs +--- a/src/header/signal/mod.rs 2026-04-15 09:40:30.420306210 +0100 ++++ b/src/header/signal/mod.rs 2026-04-15 09:46:42.011891206 +0100 +@@ -32,6 +32,9 @@ + #[path = "redox.rs"] pub mod sys; +mod signalfd; @@ -10,10 +11,9 @@ diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs type SigSet = BitSet<[u64; 1]>; pub(crate) const SIG_DFL: usize = 0; -diff --git a/src/header/signal/signalfd.rs b/src/header/signal/signalfd.rs -new file mode 100644 ---- /dev/null -+++ b/src/header/signal/signalfd.rs +diff -ruN a/src/header/signal/signalfd.rs b/src/header/signal/signalfd.rs +--- a/src/header/signal/signalfd.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/signal/signalfd.rs 2026-04-15 09:46:42.011930569 +0100 @@ -0,0 +1,103 @@ +use core::{mem, ptr}; + diff --git a/local/patches/relibc/P3-sysv-ipc.patch b/local/patches/relibc/P3-sysv-ipc.patch new file mode 100644 index 00000000..7eff024f --- /dev/null +++ b/local/patches/relibc/P3-sysv-ipc.patch @@ -0,0 +1,95 @@ +diff -ruN a/src/header/mod.rs b/src/header/mod.rs +--- a/src/header/mod.rs 2026-04-15 09:55:11.441949342 +0100 ++++ b/src/header/mod.rs 2026-04-15 09:57:28.904091552 +0100 +@@ -92,14 +92,14 @@ + pub mod sys_eventfd; + pub mod sys_file; + pub mod sys_ioctl; +-// TODO: sys/ipc.h ++pub mod sys_ipc; + pub mod sys_mman; + // TODO: sys/msg.h + pub mod sys_ptrace; + pub mod sys_resource; + pub mod sys_select; +-// TODO: sys/sem.h +-// TODO: sys/shm.h ++pub mod sys_sem; ++pub mod sys_shm; + pub mod sys_socket; + pub mod sys_stat; + pub mod sys_statvfs; +diff -ruN a/src/header/sys_ipc/cbindgen.toml b/src/header/sys_ipc/cbindgen.toml +--- a/src/header/sys_ipc/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_ipc/cbindgen.toml 2026-04-15 09:57:28.904120977 +0100 +@@ -0,0 +1,9 @@ ++sys_includes = ["sys/types.h"] ++include_guard = "_SYS_IPC_H" ++language = "C" ++style = "Tag" ++no_includes = true ++cpp_compat = true ++ ++[enum] ++prefix_with_name = true +diff -ruN a/src/header/sys_ipc/mod.rs b/src/header/sys_ipc/mod.rs +--- a/src/header/sys_ipc/mod.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_ipc/mod.rs 2026-04-15 09:57:28.904159138 +0100 +@@ -0,0 +1,31 @@ ++//! `sys/ipc.h` implementation. ++ ++use crate::platform::types::{c_int, c_ushort}; ++ ++pub type key_t = c_int; ++ ++pub const IPC_PRIVATE: key_t = 0; ++pub const IPC_CREAT: c_int = 0o1000; ++pub const IPC_EXCL: c_int = 0o2000; ++pub const IPC_NOWAIT: c_int = 0o4000; ++ ++pub const IPC_RMID: c_int = 0; ++pub const IPC_SET: c_int = 1; ++pub const IPC_STAT: c_int = 2; ++ ++#[repr(C)] ++#[derive(Clone, Copy, Default)] ++pub struct ipc_perm { ++ pub __key: key_t, ++ pub uid: c_ushort, ++ pub gid: c_ushort, ++ pub cuid: c_ushort, ++ pub cgid: c_ushort, ++ pub mode: c_ushort, ++ pub __seq: c_ushort, ++} ++ ++#[unsafe(no_mangle)] ++pub extern "C" fn _cbindgen_export_ipc_perm(value: ipc_perm) { ++ let _ = value; ++} +diff -ruN a/src/header/sys_sem/cbindgen.toml b/src/header/sys_sem/cbindgen.toml +--- a/src/header/sys_sem/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_sem/cbindgen.toml 2026-04-15 09:57:28.904183804 +0100 +@@ -0,0 +1,9 @@ ++sys_includes = ["sys/types.h", "sys/ipc.h", "stdint.h"] ++include_guard = "_SYS_SEM_H" ++language = "C" ++style = "Tag" ++no_includes = true ++cpp_compat = true ++ ++[enum] ++prefix_with_name = true +diff -ruN a/src/header/sys_shm/cbindgen.toml b/src/header/sys_shm/cbindgen.toml +--- a/src/header/sys_shm/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_shm/cbindgen.toml 2026-04-15 09:57:28.904207067 +0100 +@@ -0,0 +1,9 @@ ++sys_includes = ["sys/types.h", "sys/ipc.h", "sys/mman.h", "stdint.h"] ++include_guard = "_SYS_SHM_H" ++language = "C" ++style = "Tag" ++no_includes = true ++cpp_compat = true ++ ++[enum] ++prefix_with_name = true diff --git a/local/patches/relibc/P3-sysv-sem-impl.patch b/local/patches/relibc/P3-sysv-sem-impl.patch new file mode 100644 index 00000000..98a27133 --- /dev/null +++ b/local/patches/relibc/P3-sysv-sem-impl.patch @@ -0,0 +1,180 @@ +diff -ruN a/src/header/sys_sem/mod.rs b/src/header/sys_sem/mod.rs +--- a/src/header/sys_sem/mod.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_sem/mod.rs 2026-04-15 09:52:34.039784454 +0100 +@@ -0,0 +1,176 @@ ++//! `sys/sem.h` implementation. ++ ++use alloc::{collections::BTreeMap, ffi::CString, format}; ++use core::{mem, ptr, sync::atomic::{AtomicI32, Ordering}}; ++ ++use crate::{ ++ header::{ ++ errno::{EFAULT, EINVAL, ENOENT}, ++ fcntl::{O_CREAT, O_EXCL, O_RDWR}, ++ sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_NOWAIT, IPC_PRIVATE, IPC_RMID, ipc_perm, key_t}, ++ sys_mman::{MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink}, ++ unistd::close, ++ }, ++ out::Out, ++ platform::{ERRNO, Pal, Sys, types::{c_int, c_short, c_ushort, c_void}}, ++ sync::{Mutex, Semaphore}, ++}; ++ ++pub const GETVAL: c_int = 12; ++pub const SETVAL: c_int = 16; ++pub const SEM_UNDO: c_short = 0x1000; ++ ++#[repr(C)] ++#[derive(Clone, Copy, Default)] ++pub struct sembuf { ++ pub sem_num: c_ushort, ++ pub sem_op: c_short, ++ pub sem_flg: c_short, ++} ++ ++#[repr(C)] ++#[derive(Clone, Copy, Default)] ++pub struct semid_ds { ++ pub sem_perm: ipc_perm, ++ pub sem_nsems: c_ushort, ++} ++ ++#[repr(C)] ++pub union semun { ++ pub val: c_int, ++ pub buf: *mut semid_ds, ++} ++ ++#[repr(C)] ++struct SharedSemSet { ++ sem: Semaphore, ++} ++ ++#[derive(Clone)] ++struct SemMeta { ++ path: CString, ++ nsems: c_ushort, ++} ++ ++static NEXT_SEMID: AtomicI32 = AtomicI32::new(1); ++static SEM_REGISTRY: Mutex> = Mutex::new(BTreeMap::new()); ++static SEM_ATTACHMENTS: Mutex> = Mutex::new(BTreeMap::new()); ++ ++fn key_path(key: key_t) -> CString { CString::new(format!("/relibc-sysv-sem-{key:x}")).unwrap() } ++fn id_path(id: c_int) -> CString { CString::new(format!("/relibc-sysv-sem-id-{id:x}")).unwrap() } ++ ++fn open_flags_from_ipc(semflg: c_int) -> c_int { ++ let mut flags = O_RDWR; ++ if semflg & IPC_CREAT == IPC_CREAT { flags |= O_CREAT; } ++ if semflg & IPC_EXCL == IPC_EXCL { flags |= O_EXCL; } ++ flags ++} ++ ++unsafe fn map_sem(path: &CString, create: bool, initial: c_int) -> Result<*mut SharedSemSet, c_int> { ++ let fd = unsafe { shm_open(path.as_ptr(), if create { O_CREAT | O_RDWR } else { O_RDWR }, 0o600) }; ++ if fd < 0 { return Err(ERRNO.get()); } ++ let mut st = crate::header::sys_stat::stat::default(); ++ if Sys::fstat(fd, Out::from_mut(&mut st)).is_err() { let _ = close(fd); return Err(ERRNO.get()); } ++ if st.st_size == 0 { ++ if Sys::ftruncate(fd, mem::size_of::() as i64).is_err() { let _ = close(fd); return Err(ERRNO.get()); } ++ } ++ let ptr = unsafe { mmap(ptr::null_mut(), mem::size_of::(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) }; ++ let _ = close(fd); ++ if ptr == MAP_FAILED { return Err(ERRNO.get()); } ++ let set = ptr.cast::(); ++ if create && st.st_size == 0 { unsafe { set.write(SharedSemSet { sem: Semaphore::new(initial as u32) }) }; } ++ Ok(set) ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn semget(key: key_t, nsems: c_int, semflg: c_int) -> c_int { ++ if nsems != 1 { ERRNO.set(EINVAL); return -1; } ++ let semid = NEXT_SEMID.fetch_add(1, Ordering::SeqCst); ++ let path = if key == IPC_PRIVATE { id_path(semid) } else { key_path(key) }; ++ let fd = unsafe { shm_open(path.as_ptr(), open_flags_from_ipc(semflg), (semflg & 0o777) as c_int) }; ++ if fd < 0 { return -1; } ++ let _ = close(fd); ++ SEM_REGISTRY.lock().insert(semid, SemMeta { path, nsems: 1 }); ++ semid ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn semop(semid: c_int, sops: *mut sembuf, nsops: usize) -> c_int { ++ if sops.is_null() { ERRNO.set(EFAULT); return -1; } ++ if nsops == 0 { return 0; } ++ let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() }; ++ let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; }; ++ let set = { ++ let mut attachments = SEM_ATTACHMENTS.lock(); ++ if let Some((ptr, _)) = attachments.get(&semid) { *ptr as *mut SharedSemSet } else { ++ let ptr = match unsafe { map_sem(&meta.path, false, 0) } { Ok(ptr) => ptr, Err(_) => return -1 }; ++ attachments.insert(semid, (ptr as usize, 1)); ++ ptr ++ } ++ }; ++ let ops = unsafe { core::slice::from_raw_parts(sops, nsops) }; ++ for op in ops { ++ if op.sem_num != 0 { ERRNO.set(EINVAL); return -1; } ++ if op.sem_op > 0 { unsafe { (*set).sem.post(op.sem_op as u32) }; continue; } ++ if op.sem_op == 0 { ++ if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short { ++ if unsafe { (*set).sem.value() } != 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; } ++ } else if unsafe { (*set).sem.value() } != 0 { ++ ERRNO.set(crate::header::errno::EAGAIN); ++ return -1; ++ } ++ continue; ++ } ++ for _ in 0..(-op.sem_op) { ++ if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short { ++ if unsafe { (*set).sem.try_wait() } == 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; } ++ } else if unsafe { (*set).sem.wait(None, crate::header::time::CLOCK_MONOTONIC) }.is_err() { ++ ERRNO.set(crate::header::errno::EINTR); ++ return -1; ++ } ++ } ++ } ++ 0 ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn semctl(semid: c_int, semnum: c_int, cmd: c_int, mut args: ...) -> c_int { ++ if semnum != 0 { ERRNO.set(EINVAL); return -1; } ++ let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() }; ++ let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; }; ++ match cmd { ++ IPC_RMID => { ++ let _ = unsafe { shm_unlink(meta.path.as_ptr()) }; ++ SEM_REGISTRY.lock().remove(&semid); ++ if let Some((ptr, _)) = SEM_ATTACHMENTS.lock().remove(&semid) { ++ let _ = unsafe { munmap((ptr as *mut SharedSemSet).cast::(), mem::size_of::()) }; ++ } ++ 0 ++ } ++ GETVAL => { ++ let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } }; ++ let value = unsafe { (*set).sem.value() as c_int }; ++ let _ = unsafe { munmap(set.cast::(), mem::size_of::()) }; ++ value ++ } ++ SETVAL => { ++ let val = unsafe { args.arg::() }; ++ let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } }; ++ let current = unsafe { (*set).sem.value() as c_int }; ++ if val > current { unsafe { (*set).sem.post((val - current) as u32) }; } else { ++ for _ in 0..(current - val) { ++ if unsafe { (*set).sem.try_wait() } == 0 { break; } ++ } ++ } ++ let _ = unsafe { munmap(set.cast::(), mem::size_of::()) }; ++ 0 ++ } ++ crate::header::sys_ipc::IPC_STAT => { ++ let buf = unsafe { args.arg::<*mut semid_ds>() }; ++ if buf.is_null() { ERRNO.set(EFAULT); return -1; } ++ unsafe { *buf = semid_ds { sem_perm: ipc_perm::default(), sem_nsems: meta.nsems }; } ++ 0 ++ } ++ _ => { ERRNO.set(EINVAL); -1 } ++ } ++} diff --git a/local/patches/relibc/P3-sysv-shm-impl.patch b/local/patches/relibc/P3-sysv-shm-impl.patch new file mode 100644 index 00000000..7b9d3428 --- /dev/null +++ b/local/patches/relibc/P3-sysv-shm-impl.patch @@ -0,0 +1,131 @@ +diff -ruN a/src/header/sys_shm/mod.rs b/src/header/sys_shm/mod.rs +--- a/src/header/sys_shm/mod.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_shm/mod.rs 2026-04-15 09:52:34.038301738 +0100 +@@ -0,0 +1,127 @@ ++//! `sys/shm.h` implementation. ++ ++use alloc::{collections::BTreeMap, ffi::CString, format}; ++use core::{ptr, sync::atomic::{AtomicI32, Ordering}}; ++ ++use crate::{ ++ header::{ ++ errno::{EACCES, EFAULT, EINVAL, ENOENT, ENOSYS}, ++ fcntl::{O_CREAT, O_EXCL, O_RDWR}, ++ sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_PRIVATE, IPC_RMID, ipc_perm, key_t}, ++ sys_mman::{MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink}, ++ sys_stat::stat, ++ unistd::close, ++ }, ++ out::Out, ++ platform::{ERRNO, Pal, Sys, types::{c_int, c_ushort, c_void, size_t, time_t}}, ++ sync::Mutex, ++}; ++ ++pub const SHM_RDONLY: c_int = 0o10000; ++pub const SHM_RND: c_int = 0o20000; ++ ++#[repr(C)] ++#[derive(Clone, Copy)] ++pub struct shmid_ds { ++ pub shm_perm: ipc_perm, ++ pub shm_segsz: size_t, ++ pub shm_atime: time_t, ++ pub shm_dtime: time_t, ++ pub shm_ctime: time_t, ++ pub shm_cpid: c_int, ++ pub shm_lpid: c_int, ++ pub shm_nattch: c_ushort, ++} ++ ++impl Default for shmid_ds { ++ fn default() -> Self { ++ Self { shm_perm: ipc_perm::default(), shm_segsz: 0, shm_atime: 0, shm_dtime: 0, shm_ctime: 0, shm_cpid: 0, shm_lpid: 0, shm_nattch: 0 } ++ } ++} ++ ++#[derive(Clone)] ++struct SegmentMeta { ++ path: CString, ++ size: size_t, ++ removed: bool, ++} ++ ++static NEXT_SHMID: AtomicI32 = AtomicI32::new(1); ++static SHM_REGISTRY: Mutex> = Mutex::new(BTreeMap::new()); ++static SHM_ATTACHMENTS: Mutex> = Mutex::new(BTreeMap::new()); ++ ++fn key_path(key: key_t) -> CString { CString::new(format!("/relibc-sysv-shm-{key:x}")).unwrap() } ++fn id_path(id: c_int) -> CString { CString::new(format!("/relibc-sysv-shm-id-{id:x}")).unwrap() } ++ ++fn open_flags_from_ipc(shmflg: c_int) -> c_int { ++ let mut flags = O_RDWR; ++ if shmflg & IPC_CREAT == IPC_CREAT { flags |= O_CREAT; } ++ if shmflg & IPC_EXCL == IPC_EXCL { flags |= O_EXCL; } ++ flags ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn shmget(key: key_t, size: size_t, shmflg: c_int) -> c_int { ++ let shmid = NEXT_SHMID.fetch_add(1, Ordering::SeqCst); ++ let path = if key == IPC_PRIVATE { id_path(shmid) } else { key_path(key) }; ++ let fd = unsafe { shm_open(path.as_ptr(), open_flags_from_ipc(shmflg), (shmflg & 0o777) as c_int) }; ++ if fd < 0 { return -1; } ++ ++ let mut st = stat::default(); ++ if Sys::fstat(fd, Out::from_mut(&mut st)).is_err() { let _ = close(fd); return -1; } ++ if st.st_size == 0 && size > 0 { ++ if Sys::ftruncate(fd, size as i64).is_err() { let _ = close(fd); return -1; } ++ } ++ let _ = close(fd); ++ SHM_REGISTRY.lock().insert(shmid, SegmentMeta { path, size, removed: false }); ++ shmid ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn shmat(shmid: c_int, shmaddr: *const c_void, shmflg: c_int) -> *mut c_void { ++ let meta = { let registry = SHM_REGISTRY.lock(); registry.get(&shmid).cloned() }; ++ let Some(meta) = meta else { ERRNO.set(ENOENT); return MAP_FAILED; }; ++ if !shmaddr.is_null() { ERRNO.set(ENOSYS); return MAP_FAILED; } ++ let mut prot = PROT_READ; ++ if shmflg & SHM_RDONLY == 0 { prot |= PROT_WRITE; } ++ let fd = unsafe { shm_open(meta.path.as_ptr(), O_RDWR, 0) }; ++ if fd < 0 { return MAP_FAILED; } ++ let ptr = unsafe { mmap(ptr::null_mut(), meta.size, prot, MAP_SHARED, fd, 0) }; ++ let _ = close(fd); ++ if ptr == MAP_FAILED { return MAP_FAILED; } ++ SHM_ATTACHMENTS.lock().insert(ptr as usize, (shmid, meta.size)); ++ ptr ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn shmdt(shmaddr: *const c_void) -> c_int { ++ if shmaddr.is_null() { ERRNO.set(EFAULT); return -1; } ++ let Some((_, size)) = SHM_ATTACHMENTS.lock().remove(&(shmaddr as usize)) else { ERRNO.set(EINVAL); return -1; }; ++ unsafe { munmap(shmaddr.cast_mut(), size) } ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn shmctl(shmid: c_int, cmd: c_int, buf: *mut shmid_ds) -> c_int { ++ let mut registry = SHM_REGISTRY.lock(); ++ let Some(meta) = registry.get_mut(&shmid) else { ERRNO.set(ENOENT); return -1; }; ++ match cmd { ++ IPC_RMID => { meta.removed = true; unsafe { shm_unlink(meta.path.as_ptr()) } } ++ crate::header::sys_ipc::IPC_STAT => { ++ if buf.is_null() { ERRNO.set(EFAULT); return -1; } ++ unsafe { ++ *buf = shmid_ds { ++ shm_perm: ipc_perm::default(), ++ shm_segsz: meta.size, ++ shm_atime: 0, ++ shm_dtime: 0, ++ shm_ctime: 0, ++ shm_cpid: 0, ++ shm_lpid: 0, ++ shm_nattch: SHM_ATTACHMENTS.lock().values().filter(|(id, _)| *id == shmid).count() as c_ushort, ++ }; ++ } ++ 0 ++ } ++ _ => { ERRNO.set(EACCES); -1 } ++ } ++} diff --git a/local/patches/relibc/P3-timerfd.patch b/local/patches/relibc/P3-timerfd.patch index 143a1be3..62e5c510 100644 --- a/local/patches/relibc/P3-timerfd.patch +++ b/local/patches/relibc/P3-timerfd.patch @@ -1,18 +1,18 @@ -diff --git a/src/header/mod.rs b/src/header/mod.rs ---- a/src/header/mod.rs -+++ b/src/header/mod.rs -@@ -100,5 +100,6 @@ pub mod sys_socket; +diff -ruN a/src/header/mod.rs b/src/header/mod.rs +--- a/src/header/mod.rs 2026-04-15 09:58:03.811510680 +0100 ++++ b/src/header/mod.rs 2026-04-15 09:59:40.902089070 +0100 +@@ -103,6 +103,7 @@ pub mod sys_stat; pub mod sys_statvfs; pub mod sys_time; +pub mod sys_timerfd; #[deprecated] pub mod sys_timeb; -diff --git a/src/header/sys_timerfd/cbindgen.toml b/src/header/sys_timerfd/cbindgen.toml -new file mode 100644 ---- /dev/null -+++ b/src/header/sys_timerfd/cbindgen.toml -@@ -0,0 +1,9 @@ + //pub mod sys_times; +diff -ruN a/src/header/sys_timerfd/cbindgen.toml b/src/header/sys_timerfd/cbindgen.toml +--- a/src/header/sys_timerfd/cbindgen.toml 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_timerfd/cbindgen.toml 2026-04-15 09:59:40.902120449 +0100 +@@ -0,0 +1,12 @@ +sys_includes = ["time.h"] +include_guard = "_SYS_TIMERFD_H" +language = "C" @@ -22,11 +22,13 @@ new file mode 100644 + +[enum] +prefix_with_name = true -diff --git a/src/header/sys_timerfd/mod.rs b/src/header/sys_timerfd/mod.rs -new file mode 100644 ---- /dev/null -+++ b/src/header/sys_timerfd/mod.rs -@@ -0,0 +1,89 @@ ++ ++[export.rename] ++"itimerspec" = "struct itimerspec" +diff -ruN a/src/header/sys_timerfd/mod.rs b/src/header/sys_timerfd/mod.rs +--- a/src/header/sys_timerfd/mod.rs 1970-01-01 00:00:00.000000000 +0000 ++++ b/src/header/sys_timerfd/mod.rs 2026-04-15 09:59:40.902160103 +0100 +@@ -0,0 +1,94 @@ +//! `sys/timerfd.h` implementation. +//! +//! Non-POSIX, see . @@ -53,6 +55,7 @@ new file mode 100644 +pub const TFD_CLOEXEC: c_int = 0x80000; +pub const TFD_NONBLOCK: c_int = 0x800; +pub const TFD_TIMER_ABSTIME: c_int = 0x1; ++pub const TFD_TIMER_CANCEL_ON_SET: c_int = 0x2; + +fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> { + match Sys::read(fd, buf)? { @@ -92,7 +95,11 @@ new file mode 100644 + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const itimerspec, old: *mut itimerspec) -> c_int { -+ if flags & !TFD_TIMER_ABSTIME != 0 { ++ let supported = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET; ++ if flags & !supported != 0 { ++ return Err::(Errno(EINVAL)).or_minus_one_errno(); ++ } ++ if flags & TFD_TIMER_CANCEL_ON_SET != 0 && flags & TFD_TIMER_ABSTIME == 0 { + return Err::(Errno(EINVAL)).or_minus_one_errno(); + } + if new.is_null() { diff --git a/local/patches/relibc/P3-waitid-header.patch b/local/patches/relibc/P3-waitid-header.patch new file mode 100644 index 00000000..d5c71a68 --- /dev/null +++ b/local/patches/relibc/P3-waitid-header.patch @@ -0,0 +1,19 @@ +diff --git a/src/header/sys_wait/cbindgen.toml b/src/header/sys_wait/cbindgen.toml +index 807d8fb3..047dc3e9 100644 +--- a/src/header/sys_wait/cbindgen.toml ++++ b/src/header/sys_wait/cbindgen.toml +@@ -1,4 +1,4 @@ +-sys_includes = ["sys/types.h", "sys/resource.h"] ++sys_includes = ["sys/types.h", "sys/resource.h", "signal.h"] + include_guard = "_SYS_WAIT_H" + trailer = """ + #define WEXITSTATUS(s) (((s) >> 8) & 0xff) +@@ -7,7 +7,7 @@ trailer = """ + #define WCOREDUMP(s) (((s) & 0x80) != 0) + #define WIFEXITED(s) (((s) & 0x7f) == 0) + #define WIFSTOPPED(s) (((s) & 0xff) == 0x7f) +-#define WIFSIGNALED(s) (((((s) & 0x7f) + 1U) & 0x7f) >= 2) // Ends with 1111111 or 10000000 ++#define WIFSIGNALED(s) (((((s) & 0x7f) + 1U) & 0x7f) >= 2) + #define WIFCONTINUED(s) ((s) == 0xffff) + """ + language = "C" diff --git a/local/patches/relibc/P3-waitid.patch b/local/patches/relibc/P3-waitid.patch new file mode 100644 index 00000000..489cfead --- /dev/null +++ b/local/patches/relibc/P3-waitid.patch @@ -0,0 +1,130 @@ +diff --git a/src/header/sys_wait/mod.rs b/src/header/sys_wait/mod.rs +index 11f4bf2c..91a58c5b 100644 +--- a/src/header/sys_wait/mod.rs ++++ b/src/header/sys_wait/mod.rs +@@ -4,13 +4,17 @@ + + use crate::{ + error::ResultExt, ++ header::signal::siginfo_t, + out::Out, + platform::{ +- Pal, Sys, +- types::{c_int, pid_t}, ++ ERRNO, Pal, Sys, ++ types::{c_int, c_uint, pid_t}, + }, + }; + ++pub type idtype_t = c_int; ++pub type id_t = c_uint; ++ + pub const WNOHANG: c_int = 1; + pub const WUNTRACED: c_int = 2; + +@@ -24,27 +28,89 @@ pub const __WALL: c_int = 0x4000_0000; + #[allow(overflowing_literals)] + pub const __WCLONE: c_int = 0x8000_0000; + +-/// See . ++pub const P_ALL: idtype_t = 0; ++pub const P_PID: idtype_t = 1; ++pub const P_PGID: idtype_t = 2; ++ ++pub const CLD_EXITED: c_int = 1; ++pub const CLD_KILLED: c_int = 2; ++pub const CLD_DUMPED: c_int = 3; ++pub const CLD_TRAPPED: c_int = 4; ++pub const CLD_STOPPED: c_int = 5; ++pub const CLD_CONTINUED: c_int = 6; ++ ++fn wexitstatus(status: c_int) -> c_int { (status >> 8) & 0xff } ++fn wtermsig(status: c_int) -> c_int { status & 0x7f } ++fn wstopsig(status: c_int) -> c_int { wexitstatus(status) } ++fn wcoredump(status: c_int) -> bool { (status & 0x80) != 0 } ++fn wifexited(status: c_int) -> bool { (status & 0x7f) == 0 } ++fn wifstopped(status: c_int) -> bool { (status & 0xff) == 0x7f } ++fn wifcontinued(status: c_int) -> bool { status == 0xffff } ++ + #[unsafe(no_mangle)] + pub unsafe extern "C" fn wait(stat_loc: *mut c_int) -> pid_t { + unsafe { waitpid(!0, stat_loc, 0) } + } + +-/* +- * TODO: implement idtype_t, id_t, and siginfo_t +- * +- * #[unsafe(no_mangle)] +- * pub unsafe extern "C" fn waitid( +- * idtype: idtype_t, +- * id: id_t, +- * infop: siginfo_t, +- * options: c_int +- * ) -> c_int { +- * unimplemented!(); +- * } +- */ +- +-/// See . ++fn map_waitid_target(idtype: idtype_t, id: id_t) -> Option { ++ match idtype { ++ P_ALL => Some(-1), ++ P_PID => Some(id as pid_t), ++ P_PGID => Some(if id == 0 { 0 } else { -(id as pid_t) }), ++ _ => None, ++ } ++} ++ ++#[unsafe(no_mangle)] ++pub unsafe extern "C" fn waitid( ++ idtype: idtype_t, ++ id: id_t, ++ infop: *mut siginfo_t, ++ options: c_int, ++) -> c_int { ++ if infop.is_null() { ++ ERRNO.set(crate::header::errno::EFAULT); ++ return -1; ++ } ++ ++ let Some(pid_target) = map_waitid_target(idtype, id) else { ++ ERRNO.set(crate::header::errno::EINVAL); ++ return -1; ++ }; ++ ++ let mut status = 0; ++ let pid = Sys::waitpid(pid_target, Some(Out::from_mut(&mut status)), options).or_minus_one_errno(); ++ if pid < 0 { ++ return -1; ++ } ++ ++ unsafe { *infop = core::mem::zeroed() }; ++ if pid == 0 { ++ return 0; ++ } ++ ++ unsafe { ++ (*infop).si_pid = pid; ++ (*infop).si_signo = crate::header::signal::SIGCHLD as c_int; ++ (*infop).si_errno = 0; ++ if wifexited(status) { ++ (*infop).si_code = CLD_EXITED; ++ (*infop).si_status = wexitstatus(status); ++ } else if wifstopped(status) { ++ (*infop).si_code = CLD_STOPPED; ++ (*infop).si_status = wstopsig(status); ++ } else if wifcontinued(status) { ++ (*infop).si_code = CLD_CONTINUED; ++ (*infop).si_status = crate::header::signal::SIGCONT as c_int; ++ } else { ++ (*infop).si_status = wtermsig(status); ++ (*infop).si_code = if wcoredump(status) { CLD_DUMPED } else { CLD_KILLED }; ++ } ++ } ++ ++ 0 ++} ++ + #[unsafe(no_mangle)] + pub unsafe extern "C" fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t { + Sys::waitpid(pid, unsafe { Out::nullable(stat_loc) }, options).or_minus_one_errno()