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
This commit is contained in:
2026-05-05 21:12:08 +01:00
parent f31522130f
commit 702ec7efac
14 changed files with 1201 additions and 279 deletions
@@ -1,10 +1,7 @@
mod scheme;
use std::io::Write;
use std::thread;
use std::time::Duration;
use scheme::ImeScheme;
use std::io::Write;
fn log_msg(level: &str, msg: &str) {
let _ = writeln!(std::io::stderr(), "[ime] {} {}", level, msg);
@@ -13,8 +10,7 @@ fn log_msg(level: &str, msg: &str) {
fn main() {
let mut scheme = ImeScheme::new();
let socket = redox_scheme::Socket::nonblock("ime")
.expect("ime: failed to register scheme:ime");
let socket = redox_scheme::Socket::create("ime").expect("ime: failed to register scheme:ime");
log_msg("INFO", "registered scheme:ime");
loop {
@@ -25,15 +21,16 @@ fn main() {
break;
}
Err(e) => {
log_msg("WARN", &format!("scheme read error (ignoring): {}", e));
thread::sleep(Duration::from_millis(100));
continue;
log_msg("ERROR", &format!("scheme read error: {}", e));
break;
}
};
match request.handle_scheme_block_mut(&mut scheme) {
Ok(response) => {
if let Err(e) = socket.write_response(response, redox_scheme::SignalBehavior::Restart) {
if let Err(e) =
socket.write_response(response, redox_scheme::SignalBehavior::Restart)
{
log_msg("ERROR", &format!("failed to write response: {}", e));
}
}
@@ -93,17 +93,50 @@ impl BasicLatinEngine {
}
fn scancode_to_char(scancode: u8, shift: bool) -> Option<char> {
let lower = match scancode {
0x02 => '1', 0x03 => '2', 0x04 => '3', 0x05 => '4', 0x06 => '5',
0x07 => '6', 0x08 => '7', 0x09 => '8', 0x0A => '9', 0x0B => '0',
0x10 => 'q', 0x11 => 'w', 0x12 => 'e', 0x13 => 'r', 0x14 => 't',
0x15 => 'y', 0x16 => 'u', 0x17 => 'i', 0x18 => 'o', 0x19 => 'p',
0x1E => 'a', 0x1F => 's', 0x20 => 'd', 0x21 => 'f', 0x22 => 'g',
0x23 => 'h', 0x24 => 'j', 0x25 => 'k', 0x26 => 'l',
0x2C => 'z', 0x2D => 'x', 0x2E => 'c', 0x2F => 'v', 0x30 => 'b',
0x31 => 'n', 0x32 => 'm',
0x27 => ';', 0x28 => '\'', 0x29 => '`',
0x33 => ',', 0x34 => '.', 0x35 => '/',
0x0C => '-', 0x0D => '=',
0x02 => '1',
0x03 => '2',
0x04 => '3',
0x05 => '4',
0x06 => '5',
0x07 => '6',
0x08 => '7',
0x09 => '8',
0x0A => '9',
0x0B => '0',
0x10 => 'q',
0x11 => 'w',
0x12 => 'e',
0x13 => 'r',
0x14 => 't',
0x15 => 'y',
0x16 => 'u',
0x17 => 'i',
0x18 => 'o',
0x19 => 'p',
0x1E => 'a',
0x1F => 's',
0x20 => 'd',
0x21 => 'f',
0x22 => 'g',
0x23 => 'h',
0x24 => 'j',
0x25 => 'k',
0x26 => 'l',
0x2C => 'z',
0x2D => 'x',
0x2E => 'c',
0x2F => 'v',
0x30 => 'b',
0x31 => 'n',
0x32 => 'm',
0x27 => ';',
0x28 => '\'',
0x29 => '`',
0x33 => ',',
0x34 => '.',
0x35 => '/',
0x0C => '-',
0x0D => '=',
_ => return None,
};
if shift {
@@ -157,8 +190,10 @@ impl ImeEngine for BasicLatinEngine {
let is_dead_trigger = scancode == self.compose_scancode;
if is_dead_trigger {
let dead_keys = [
('\u{0300}', "grave"), ('\u{0301}', "acute"),
('\u{0302}', "circumflex"), ('\u{0308}', "diaeresis"),
('\u{0300}', "grave"),
('\u{0301}', "acute"),
('\u{0302}', "circumflex"),
('\u{0308}', "diaeresis"),
('\u{030C}', "caron"),
];
let (dk, _name) = dead_keys[self.dead_key_index % dead_keys.len()];
@@ -206,6 +241,9 @@ impl ImeScheme {
self.state.composing = false;
self.state.preedit.clear();
}
if result.candidates_changed {
self.state.composing = !self.engine.candidates().is_empty();
}
if !result.preedit.is_empty() {
self.state.composing = true;
self.state.preedit = result.preedit;
@@ -235,13 +273,11 @@ impl redox_scheme::SchemeBlockMut for ImeScheme {
let content: Vec<u8> = match &handle.kind {
HandleKind::Root => "state\ncompose\ncandidates\n".as_bytes().to_vec(),
HandleKind::State => {
format!(
"composing={}\npreedit={}\ncommitted={}\n",
self.state.composing, self.state.preedit, self.state.committed
)
.into_bytes()
}
HandleKind::State => format!(
"composing={}\npreedit={}\ncommitted={}\n",
self.state.composing, self.state.preedit, self.state.committed
)
.into_bytes(),
HandleKind::Compose => "basic-latin\n".as_bytes().to_vec(),
HandleKind::Candidates => {
let mut out = String::new();
@@ -267,9 +303,34 @@ impl redox_scheme::SchemeBlockMut for ImeScheme {
match &handle.kind {
HandleKind::Compose => {
let input = String::from_utf8_lossy(buf);
if input.trim() == "reset" {
self.engine.reset();
self.state = InputState::default();
for line in input.lines() {
let line = line.trim();
if line == "reset" {
self.engine.reset();
self.state = InputState::default();
continue;
}
let mut fields = line.split_whitespace();
let Some("feed") = fields.next() else {
continue;
};
let Some(scancode) = fields.next().and_then(|field| field.parse::<u8>().ok())
else {
continue;
};
let Some(pressed) = fields.next().and_then(|field| field.parse::<bool>().ok())
else {
continue;
};
let shift = fields
.next()
.and_then(|field| field.parse::<bool>().ok())
.unwrap_or(false);
let altgr = fields
.next()
.and_then(|field| field.parse::<bool>().ok())
.unwrap_or(false);
self.feed(scancode, pressed, shift, altgr);
}
Ok(Some(buf.len()))
}