9.1 KiB
Relibc vs GNU libc — Cross-Reference Assessment
Date: 2026-05-05 Reference: glibc 2.41 (2026-05-05 clone from sourceware.org) Relibc pinned: commit 861bbb0 with Red Bear patch chain (26 patches)
1. eventfd
glibc reference
// sysdeps/unix/sysv/linux/eventfd.c (not cloned yet — syscall wrapper)
// bits/eventfd.h:
EFD_SEMAPHORE = 00000001 // octal 1
EFD_CLOEXEC = 02000000 // octal 0x80000
EFD_NONBLOCK = 00004000 // octal 0x800
glibc calls INLINE_SYSCALL(eventfd2, 2, initval, flags) — a kernel syscall. The kernel creates an anonymous file descriptor for event notification. Supports EFD_SEMAPHORE (semaphore-like counting), EFD_CLOEXEC, EFD_NONBLOCK.
relibc current state (updated 2026-05-05 — S1-S4 implemented)
// src/header/sys_eventfd/mod.rs — 30 lines
// Full eventfd() implementation with EFD_SEMAPHORE/CLOEXEC/NONBLOCK.
// Opens scheme:event/eventfd/{initval}/{sem} via Sys::open.
Implementation shipped ✅
Kernel support: P0-eventfd-kernel.patch extends event scheme with eventfd path parsing ✅
Gaps
| Gap | Severity | Detail |
|---|---|---|
No eventfd() in relibc |
Medium | libwayland has its own inline, but relibc should be canonical |
No eventfd_read()/eventfd_write() |
Low | POSIX-adjacent convenience wrappers (glibc provides them) |
2. signalfd
glibc reference
// sysdeps/unix/sysv/linux/signalfd.c
int signalfd(int fd, const sigset_t *mask, int flags) {
return INLINE_SYSCALL(signalfd4, 4, fd, mask, __NSIG_BYTES, flags);
}
// bits/signalfd.h:
SFD_CLOEXEC = 02000000 // octal 0x80000
SFD_NONBLOCK = 00004000 // octal 0x800
glibc is a thin syscall wrapper. Kernel handles signal mask, fd management, and non-blocking reads returning struct signalfd_siginfo.
relibc current state
// src/header/signal/signalfd.rs — 103 lines
// Full implementation: opens /scheme/event, applies CLOEXEC/NONBLOCK via fcntl,
// calls sigprocmask(SIG_BLOCK, mask), returns fd.
// signalfd4 supports modifying existing fd's flags.
Flags match glibc ✅
signalfd_siginfo struct matches ✅
signalfd4 with existing fd ✅ (fcntl-based flag modification)
Prowess vs glibc
| Aspect | glibc | relibc |
|---|---|---|
| Implementation | Syscall wrapper (5 lines) | Userspace via /scheme/event (100 lines) |
| Existing fd support | Kernel handles | fcntl O_CLOEXEC/O_NONBLOCK modification ✅ |
| Errno mapping | Kernel errno | Wraps in Errno, proper EINVAL/EFAULT |
| Signal blocking | Kernel auto-blocks on read | sigprocmask(SIG_BLOCK, mask) called explicitly ✅ |
Gaps
| Gap | Severity | Detail |
|---|---|---|
| No read path | High | Nothing reads signalfd_siginfo from the fd — the /scheme/event fd is opened but signals aren't delivered through it |
| Signal delivery unverified | High | The sigprocmask(SIG_BLOCK) blocks signals but there's no evidence the kernel delivers them via the event fd |
signalfd_siginfo read not implemented |
Critical | struct signalfd_siginfo is defined but never populated via read(2) |
3. Semaphores
glibc reference
// sysdeps/pthread/sem_open.c — 216 lines
// Uses:
// __shm_get_name(name) → canonical path in /dev/shm
// O_CREAT+O_EXCL path: creates temp file, writes semaphore header, ftruncate to sizeof(sem_t), mmap
// Non-create path: open existing, __sem_check_add_mapping(name, fd) → reuse or mmap
// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE) — cancellation-safe
// va_arg for mode_t when O_CREAT
glibc uses a sophisticated named semaphore implementation:
- Name canonicalization:
__shm_get_nametransforms/name→/dev/shm/sem.name - Existing mapping reuse:
__sem_check_add_mappingchecks global list of already-mapped semaphores - Atomic creation: O_CREAT+O_EXCL with temp file, then writes header, ftruncate, mmap
- Cancellation safety:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE)around file operations - Proper mode_t: va_arg for mode when O_CREAT
- Reference counting:
__sem_check_add_mappingincrements refcount,sem_closedecrements
relibc current state
// src/header/semaphore/mod.rs — 176 lines
// Uses: shm_open(name, O_CREAT|O_EXCL|O_RDWR, mode) → ftruncate → mmap → init
// NamedSemaphore struct with RlctSempahore (futex-based)
Core mechanism works ✅ (shm_open + mmap)
sem_init/destroy/post/wait/trywait/timedwait/clockwait ✅
sem_open/close/unlink ✅ (implemented in P3-semaphore-comprehensive.patch)
Gaps vs glibc
| Gap | Severity | Detail |
|---|---|---|
| No name canonicalization | Names now prefixed with sem. before shm_open. glibc uses /dev/shm/sem.NAME equivalent. |
|
| No existing mapping reuse | Global BTreeMap<String, NamedSemEntry> with AtomicUsize refcount. sem_open reuses existing mappings, increments refcount. |
|
| No refcounting | sem_close decrements AtomicUsize, munmaps only when zero. |
|
| No cancellation safety | Low | No pthread_setcancelstate around file ops |
| va_list not parsed | Medium | sem_open hardcodes value=0 when O_CREAT, ignoring mode and initial value from varargs |
No __sem_check_add_mapping equivalent |
High | Opens named sem every time instead of reusing existing mapping |
| No O_NOFOLLOW | Low | glibc uses O_NOFOLLOW for security |
4. Cross-Cutting Gaps
Error Handling
| Area | glibc | relibc |
|---|---|---|
| errno thread-safety | TLS errno via kernel | Cell<c_int> per platform ✅ |
| errno after close | Preserved (close may overwrite) | let _ = Sys::close(fd) — ignores errors ✅ |
| EINTR | Handled in syscall wrappers | Semaphore::wait returns Result<(), c_int>. sem_wait/timedwait loop on EINTR ✅ |
sem_wait |
AS-safe (futex wait, EINTR) | EINTR retry loop ✅ |
| sem_open refcount | Mutex-protected global list | BTreeMap<String, NamedSemEntry> with AtomicUsize ✅ |
| sem_close/sem_unlink | Mutex-protected | Mutex<Option<BTreeMap<...>>> protects registry ✅ |
| signalfd mask | Per-process (kernel) | Per-call sigprocmask ✅ |
5. Priority Improvement Plan
Phase S1: Critical Correctness (1-2 weeks)
- sem_open refcounting — Add global
HashMap<String, (Arc<NamedSemaphore>, AtomicUsize)>to reuse existing mappings.sem_closedecrements refcount, munmaps only when zero. - Eventfd implementation — Implement
eventfd()via/scheme/event/eventfd/using the existing scheme mechanism. Remove libwayland's inline copy.
Phase S2: Completeness (2-3 weeks)
- signalfd read path — Implement read(2) →
signalfd_siginfostruct population. The/scheme/eventfd must deliver signal info formatted assignalfd_siginfo. - sem_open va_list — Parse
mode_tandvaluefrom varargs when O_CREAT. Requirescrate::header::stdargor manual stack walking. - sem_open name canonicalization — Prefix names with
/scheme/shm/sem.for namespace isolation.
Phase S3: Robustness (3-4 weeks)
- EINTR handling — Wrap futex waits to retry on
EINTR. - sem_open cancellation safety — Add
pthread_setcancelstatearound file ops (if pthread cancellation is supported). - eventfd semaphore mode — Implement
EFD_SEMAPHOREcounting semantics (decrements on read, blocks at 0).
Phase S4: POSIX Conformance (2-3 weeks)
- eventfd_read/eventfd_write — Convenience wrappers.
- sem_open existing-semaphore reopening — Handle
(oflag & O_CREAT) == 0path (open existing without O_EXCL). - Signalfd signal delivery verification — Runtime tests proving signals arrive via signalfd.
6. Eventfd Kernel Requirement
Unlike signalfd and sem_open which can be implemented in userspace via existing schemes (/scheme/event, shm_open), eventfd currently relies on libwayland's inline implementation that opens /scheme/event/eventfd/. A canonical relibc implementation should use the same path.
The /scheme/event kernel scheme needs:
- Support for
eventfdsub-path - EFD_SEMAPHORE counting semantics in kernel
- Non-blocking reads returning
u64count
This is a kernel change and should be tracked separately from relibc.
7. Summary
| Component | relibc Status | Matches glibc | Critical Gaps |
|---|---|---|---|
| eventfd | Full implementation | Constants ✅ | eventfd() implemented via /scheme/event/eventfd/ ✅ |
| signalfd | 103-line impl | Flags ✅, struct ✅, signalfd4 ✅ | Read path needs kernel signal delivery ⚠️ |
| sem_open | 226-line impl with refcount | Core works ✅, shm+mmap ✅ | va_list ✅, refcounting ✅, mapping reuse ✅ |
| sem_close | Refcounted munmap | Semantics correct ✅ | Atomic refcount decrement ✅ |
| sem_wait/post | Futex-based, EINTR retry | Works ✅ | EINTR loop ✅, errno returned on other errors |
Total estimated effort: 8-12 weeks for all gaps. Critical path: eventfd kernal + signalfd read + sem_open refcounting (Phase S1).