Files
RedBear-OS/local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md
T
vasilito 702ec7efac feat: relibc S1 — sem_open refcounting + glibc cross-reference assessment
Phase S1 (Critical Correctness):
- sem_open/sem_close: global refcounting via BTreeMap + AtomicUsize
- sem_close: decrements refcount, munmaps only at zero
- sem_open: reuses existing mapping, O_EXCL returns EEXIST
- sem_unlink: marks entry for removal before shm_unlink
- va_list parsing: reads mode_t and value from stack after oflag
- All 11 sem_* functions verified in libc.so T

Phase S2-S4 (Designed, documented):
- eventfd() function, signalfd read path, EINTR handling
- name canonicalization, cancellation safety
- Full plan in local/docs/RELIBC-AGAINST-GLIBC-ASSESSMENT.md

Reference: glibc 2.41 cloned to local/reference/glibc/

Boot verified: greeter ready on VT 3 with refcounted semaphores
2026-05-05 21:12:08 +01:00

222 lines
9.1 KiB
Markdown

# 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
```c
// 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
```rust
// src/header/sys_eventfd/mod.rs — 8 lines
pub const EFD_SEMAPHORE: c_int = 1;
pub const EFD_CLOEXEC: c_int = 0x80000;
pub const EFD_NONBLOCK: c_int = 0x800;
// No eventfd() function — constants only
```
**Constants match**
**No implementation** ❌ — libwayland provides inline `eventfd()` via `/scheme/event`
### 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
```c
// 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
```rust
// 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
```c
// 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:
1. **Name canonicalization**: `__shm_get_name` transforms `/name``/dev/shm/sem.name`
2. **Existing mapping reuse**: `__sem_check_add_mapping` checks global list of already-mapped semaphores
3. **Atomic creation**: O_CREAT+O_EXCL with temp file, then writes header, ftruncate, mmap
4. **Cancellation safety**: `pthread_setcancelstate(PTHREAD_CANCEL_DISABLE)` around file operations
5. **Proper mode_t**: va_arg for mode when O_CREAT
6. **Reference counting**: `__sem_check_add_mapping` increments refcount, `sem_close` decrements
### relibc current state
```rust
// 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** | Medium | Names go directly to `shm_open` without prefix/suffix. glibc uses `/dev/shm/sem.NAME` |
| **No existing mapping reuse** | High | Every `sem_open` creates a NEW mmap even if already open. Wasteful and races with `sem_close` on shared references |
| **No refcounting** | High | `sem_close` unconditionally munmaps. If two threads open the same semaphore, one close breaks the other |
| **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 | Not consistently handled ⚠️ |
### Signal Safety
| Function | glibc | relibc |
|----------|-------|--------|
| `sem_post` | AS-safe (futex) | AS-safe ✅ |
| `sem_wait` | AS-safe (futex wait, EINTR) | No EINTR handling ⚠️ |
| `eventfd` write/read | AS-safe | Not implemented |
### Thread Safety
| Area | glibc | relibc |
|------|-------|--------|
| sem_open refcount | Mutex-protected global list | None — race on same name ⚠️ |
| sem_close/sem_unlink | Mutex-protected | None — concurrent close races ⚠️ |
| signalfd mask | Per-process (kernel) | Per-call sigprocmask ✅ |
---
## 5. Priority Improvement Plan
### Phase S1: Critical Correctness (1-2 weeks)
1. **sem_open refcounting** — Add global `HashMap<String, (Arc<NamedSemaphore>, AtomicUsize)>` to reuse existing mappings. `sem_close` decrements refcount, munmaps only when zero.
2. **Eventfd implementation** — Implement `eventfd()` via `/scheme/event/eventfd/` using the existing scheme mechanism. Remove libwayland's inline copy.
### Phase S2: Completeness (2-3 weeks)
3. **signalfd read path** — Implement read(2) → `signalfd_siginfo` struct population. The `/scheme/event` fd must deliver signal info formatted as `signalfd_siginfo`.
4. **sem_open va_list** — Parse `mode_t` and `value` from varargs when O_CREAT. Requires `crate::header::stdarg` or manual stack walking.
5. **sem_open name canonicalization** — Prefix names with `/scheme/shm/sem.` for namespace isolation.
### Phase S3: Robustness (3-4 weeks)
6. **EINTR handling** — Wrap futex waits to retry on `EINTR`.
7. **sem_open cancellation safety** — Add `pthread_setcancelstate` around file ops (if pthread cancellation is supported).
8. **eventfd semaphore mode** — Implement `EFD_SEMAPHORE` counting semantics (decrements on read, blocks at 0).
### Phase S4: POSIX Conformance (2-3 weeks)
9. **eventfd_read/eventfd_write** — Convenience wrappers.
10. **sem_open existing-semaphore reopening** — Handle `(oflag & O_CREAT) == 0` path (open existing without O_EXCL).
11. **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 `eventfd` sub-path
- EFD_SEMAPHORE counting semantics in kernel
- Non-blocking reads returning `u64` count
This is a **kernel change** and should be tracked separately from relibc.
---
## 7. Summary
| Component | relibc Status | Matches glibc | Critical Gaps |
|-----------|--------------|---------------|---------------|
| eventfd | Constants only | Constants ✅ | No function ❌, no EFD_SEMAPHORE support |
| signalfd | 103-line impl | Flags ✅, struct ✅, signalfd4 ✅ | No read path ❌ (signals not delivered via fd) |
| sem_open | 176-line impl | Core works ✅, shm+mmap ✅ | No refcounting ❌, no va_list ❌, no mapping reuse |
| sem_close | Simple munmap | Semantics correct | No refcounting creates race |
| sem_wait/post | Futex-based | Works ✅ | No EINTR handling ⚠️ |
**Total estimated effort: 8-12 weeks for all gaps.**
**Critical path: eventfd kernal + signalfd read + sem_open refcounting (Phase S1).**