Preserve relibc overlay carriers

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-15 12:57:45 +01:00
parent 8ad1242ab6
commit e113e13723
10 changed files with 736 additions and 41 deletions
+22 -18
View File
@@ -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 <https://man7.org/linux/man-pages/man2/eventfd.2.html>.
@@ -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::<u8>(), mem::size_of::<u64>()) };
+ let buf = unsafe { slice::from_raw_parts_mut(value.cast::<u8>(), mem::size_of::<eventfd_t>()) };
+ 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::<u8>(), mem::size_of::<u64>()) };
+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::<u8>(), mem::size_of::<eventfd_t>()) };
+ write_exact(fd, buf).map(|()| 0).or_minus_one_errno()
+}
+92
View File
@@ -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 <assert.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <stdio.h>
+
+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 <assert.h>
+#include <stdio.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+
+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 <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/signalfd.h>
+#include <unistd.h>
+
+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;
+}
@@ -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) {}
+8 -8
View File
@@ -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};
+
+95
View File
@@ -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
+180
View File
@@ -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<BTreeMap<c_int, SemMeta>> = Mutex::new(BTreeMap::new());
+static SEM_ATTACHMENTS: Mutex<BTreeMap<c_int, (usize, c_int)>> = 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::<SharedSemSet>() as i64).is_err() { let _ = close(fd); return Err(ERRNO.get()); }
+ }
+ let ptr = unsafe { mmap(ptr::null_mut(), mem::size_of::<SharedSemSet>(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) };
+ let _ = close(fd);
+ if ptr == MAP_FAILED { return Err(ERRNO.get()); }
+ let set = ptr.cast::<SharedSemSet>();
+ 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::<c_void>(), mem::size_of::<SharedSemSet>()) };
+ }
+ 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::<c_void>(), mem::size_of::<SharedSemSet>()) };
+ value
+ }
+ SETVAL => {
+ let val = unsafe { args.arg::<c_int>() };
+ 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::<c_void>(), mem::size_of::<SharedSemSet>()) };
+ 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 }
+ }
+}
+131
View File
@@ -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<BTreeMap<c_int, SegmentMeta>> = Mutex::new(BTreeMap::new());
+static SHM_ATTACHMENTS: Mutex<BTreeMap<usize, (c_int, size_t)>> = 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 }
+ }
+}
+22 -15
View File
@@ -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 <https://man7.org/linux/man-pages/man2/timerfd_create.2.html>.
@@ -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::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
+ }
+ if flags & TFD_TIMER_CANCEL_ON_SET != 0 && flags & TFD_TIMER_ABSTIME == 0 {
+ return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
+ }
+ if new.is_null() {
@@ -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"
+130
View File
@@ -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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wait.html>.
+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 <https://pubs.opengroup.org/onlinepubs/9799919799/functions/waitpid.html>.
+fn map_waitid_target(idtype: idtype_t, id: id_t) -> Option<pid_t> {
+ 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()