Skip to main content

c/
regression_stubs.rs

1// Copyright (c) 2026 Meng Zhuo
2// SPDX-License-Identifier: BSD-3-Clause
3
4// Stubs and minimal implementations for symbols required by libc-test
5// regression cases that currently fail to link.
6
7const _SC_ARG_MAX: c_int = 0;
8const _SC_CHILD_MAX: c_int = 1;
9const _SC_CLK_TCK: c_int = 2;
10const _SC_NGROUPS_MAX: c_int = 3;
11const _SC_OPEN_MAX: c_int = 4;
12const _SC_STREAM_MAX: c_int = 5;
13const _SC_TZNAME_MAX: c_int = 6;
14const _SC_PAGE_SIZE: c_int = 30;
15const _SC_NPROCESSORS_CONF: c_int = 83;
16const _SC_NPROCESSORS_ONLN: c_int = 84;
17const _SC_PHYS_PAGES: c_int = 85;
18const _SC_AVPHYS_PAGES: c_int = 86;
19const _SC_IOV_MAX: c_int = 60;
20const _SC_SEM_NSEMS_MAX: c_int = 32;
21const _SC_SEM_VALUE_MAX: c_int = 33;
22const _SC_MQ_OPEN_MAX: c_int = 27;
23const _SC_MQ_PRIO_MAX: c_int = 28;
24const _SC_DELAYTIMER_MAX: c_int = 26;
25const _SC_THREAD_DESTRUCTOR_ITERATIONS: c_int = 73;
26const _SC_THREAD_KEYS_MAX: c_int = 74;
27const _SC_THREAD_STACK_MIN: c_int = 75;
28const _SC_LOGIN_NAME_MAX: c_int = 71;
29const _SC_TTY_NAME_MAX: c_int = 72;
30const _SC_SYMLOOP_MAX: c_int = 173;
31const _SC_HOST_NAME_MAX: c_int = 180;
32const _SC_NZERO: c_int = 109;
33const _SC_VERSION: c_int = 29;
34const _SC_2_VERSION: c_int = 46;
35const _SC_XOPEN_VERSION: c_int = 89;
36
37#[no_mangle]
38pub unsafe extern "C" fn mmap(
39    addr: *mut c_void,
40    len: usize,
41    prot: c_int,
42    flags: c_int,
43    fd: c_int,
44    off: i64,
45) -> *mut c_void {
46    let r = crate::syscall(
47        SYS_MMAP,
48        addr as c_long,
49        len as c_long,
50        prot as c_long,
51        flags as c_long,
52        fd as c_long,
53        off as c_long,
54    );
55    if r == -1 {
56        return -1isize as *mut c_void;
57    }
58    r as *mut c_void
59}
60
61#[no_mangle]
62pub unsafe extern "C" fn munmap(addr: *mut c_void, len: usize) -> c_int {
63    crate::syscall(SYS_MUNMAP, addr as c_long, len as c_long, 0, 0, 0, 0) as c_int
64}
65
66#[no_mangle]
67pub unsafe extern "C" fn pread(fd: c_int, buf: *mut c_void, count: usize, offset: i64) -> isize {
68    crate::syscall(
69        SYS_PREAD64,
70        fd as c_long,
71        buf as c_long,
72        count as c_long,
73        offset as c_long,
74        0,
75        0,
76    ) as isize
77}
78
79unsafe fn rlimit_sc(resource: c_int) -> c_long {
80    let mut lim: crate::Rlimit = core::mem::zeroed();
81    if crate::getrlimit(resource, &mut lim) != 0 {
82        return -1;
83    }
84    if lim.rlim_cur == !0u64 {
85        return -1;
86    }
87    if lim.rlim_cur > c_long::MAX as u64 {
88        return c_long::MAX;
89    }
90    lim.rlim_cur as c_long
91}
92
93unsafe fn online_cpus() -> c_long {
94    let mut set = [0u8; 128];
95    let r = crate::sched_getaffinity(0, set.len(), set.as_mut_ptr());
96    if r != 0 {
97        return 1;
98    }
99    let mut count = 0u32;
100    for b in set.iter() {
101        count += b.count_ones();
102    }
103    if count == 0 { 1 } else { count as c_long }
104}
105
106unsafe fn memory_pages(free: bool) -> c_long {
107    // struct sysinfo: totalram/freeram/bufferram/mem_unit at fixed offsets.
108    let mut info = [0u64; 16];
109    if crate::sysinfo(info.as_mut_ptr() as *mut core::ffi::c_void) != 0 {
110        return -1;
111    }
112    let total = if free {
113        info[5].saturating_add(info[7])
114    } else {
115        info[4]
116    };
117    let unit = (info[13] & 0xffff_ffff) as u64;
118    let bytes = total.saturating_mul(if unit == 0 { 1 } else { unit });
119    (bytes / 4096) as c_long
120}
121
122#[no_mangle]
123pub unsafe extern "C" fn sysconf(name: c_int) -> c_long {
124    const POSIX_VERSION: c_long = 200809;
125    match name {
126        _SC_ARG_MAX => 131072,
127        _SC_CHILD_MAX => rlimit_sc(crate::RLIMIT_NPROC),
128        _SC_CLK_TCK => 100,
129        _SC_NGROUPS_MAX => 32,
130        _SC_OPEN_MAX => rlimit_sc(crate::RLIMIT_NOFILE),
131        _SC_STREAM_MAX => -1,
132        _SC_TZNAME_MAX => 6,
133        7 | 8 => 1,                    // JOB_CONTROL, SAVED_IDS
134        9 | 11 | 12 => POSIX_VERSION,  // REALTIME_SIGNALS, TIMERS, ASYNC_IO
135        10 | 13 | 14 => -1,            // PRIORITY_SCHEDULING, PRIORITIZED_IO, SYNC_IO
136        15..=22 => POSIX_VERSION,      // FSYNC .. SHARED_MEMORY_OBJECTS
137        23 | 24 => -1,                 // AIO_LISTIO_MAX, AIO_MAX
138        25 => 0,                       // AIO_PRIO_DELTA_MAX
139        _SC_DELAYTIMER_MAX => 0x7fffffff,
140        _SC_MQ_OPEN_MAX => -1,
141        _SC_MQ_PRIO_MAX => 32768,
142        _SC_VERSION => POSIX_VERSION,
143        _SC_PAGE_SIZE => 4096,
144        31 => 30,                      // _SC_RTSIG_MAX
145        _SC_SEM_NSEMS_MAX => 256,
146        _SC_SEM_VALUE_MAX => 0x7fffffff,
147        34 | 35 => -1,                 // SIGQUEUE_MAX, TIMER_MAX
148        36 => 99, 37 => 2048, 38 => 99, 39 => 1000, // POSIX2 bc limits
149        40 => 2,                       // COLL_WEIGHTS_MAX
150        42 | 43 => -1,                 // EXPR_NEST_MAX, LINE_MAX
151        44 => 255,                     // RE_DUP_MAX
152        _SC_2_VERSION => POSIX_VERSION,
153        47 => POSIX_VERSION,           // _SC_2_C_BIND
154        48..=52 => -1,                 // 2_C_DEV .. 2_LOCALEDEF
155        _SC_IOV_MAX => 1024,
156        67 | 68 => POSIX_VERSION,      // THREADS, THREAD_SAFE_FUNCTIONS
157        69 | 70 => -1,                 // GETGR/PW_R_SIZE_MAX
158        _SC_LOGIN_NAME_MAX => 256,
159        _SC_TTY_NAME_MAX => 32,
160        _SC_THREAD_DESTRUCTOR_ITERATIONS => 4,
161        _SC_THREAD_KEYS_MAX => 128,
162        _SC_THREAD_STACK_MIN => 2048,
163        76 => -1,                      // THREAD_THREADS_MAX
164        77 | 78 | 79 => POSIX_VERSION, // ATTR_STACKADDR/SIZE, PRIO_SCHED
165        80 | 81 => -1,                 // PRIO_INHERIT/PROTECT
166        82 => POSIX_VERSION,           // PROCESS_SHARED
167        _SC_NPROCESSORS_CONF | _SC_NPROCESSORS_ONLN => online_cpus(),
168        _SC_PHYS_PAGES => memory_pages(false),
169        _SC_AVPHYS_PAGES => memory_pages(true),
170        87 | 88 => -1,                 // ATEXIT_MAX, PASS_MAX
171        _SC_XOPEN_VERSION => 700,
172        90 => 700,                     // XOPEN_XCU_VERSION
173        91 => 1,                       // XOPEN_UNIX
174        92 => -1,                      // XOPEN_CRYPT
175        93 | 94 => 1,                  // XOPEN_ENH_I18N, XOPEN_SHM
176        95 => -1,                      // 2_CHAR_TERM
177        97 => -1,                      // 2_UPE
178        98..=100 => -1,                // XOPEN_XPG2..4
179        _SC_NZERO => 20,
180        125 | 126 | 128 => -1,         // XBS5 ILP32/LPBIG
181        127 => 1,                      // XBS5 LP64_OFF64
182        129..=131 => -1,               // XOPEN legacy/realtime
183        132 | 133 => POSIX_VERSION,    // ADVISORY_INFO, BARRIERS
184        137 | 138 | 139 => POSIX_VERSION,
185        149 => POSIX_VERSION,          // MONOTONIC_CLOCK
186        153 | 154 | 155 | 157 | 159 => POSIX_VERSION,
187        160 | 161 => -1,
188        164 => POSIX_VERSION,          // TIMEOUTS
189        165 => -1,                     // TYPED_MEMORY_OBJECTS
190        168..=172 => -1,               // 2_PBS
191        _SC_SYMLOOP_MAX => 40,
192        174 => 0,                      // STREAMS
193        175 => -1,
194        176 | 177 | 179 => -1,         // V6 ILP32/LPBIG
195        178 => 1,                      // V6_LP64_OFF64
196        _SC_HOST_NAME_MAX => 255,
197        181..=184 => -1,               // TRACE
198        235 | 236 => POSIX_VERSION,    // IPV6, RAW_SOCKETS
199        237 | 238 | 240 => -1,         // V7 variants
200        239 => 1,                      // V7_LP64_OFF64
201        241..=245 => -1,               // SS_REPL_MAX, TRACE_*
202        _ => {
203            crate::__errno_location().write(EINVAL);
204            -1
205        }
206    }
207}
208
209#[no_mangle]
210pub unsafe extern "C" fn execle(path: *const c_char, arg: *const c_char, mut args: ...) -> c_int {
211    let mut argv: [*const c_char; 128] = [core::ptr::null(); 128];
212    argv[0] = arg;
213    let mut n = 1usize;
214    loop {
215        let a: *const c_char = args.next_arg();
216        if a.is_null() {
217            break;
218        }
219        if n >= argv.len() - 1 {
220            crate::__errno_location().write(E2BIG);
221            return -1;
222        }
223        argv[n] = a;
224        n += 1;
225    }
226    argv[n] = core::ptr::null();
227    let envp: *const *const c_char = args.next_arg();
228    crate::execve(path, argv.as_ptr(), envp)
229}
230
231#[no_mangle]
232pub unsafe extern "C" fn flockfile(f: *mut crate::FILE) {
233    if (*f).lock < 0 { return; }
234    let tid = crate::sys_gettid() as i32;
235    loop {
236        let v = crate::a_load(&raw const (*f).lock);
237        if v == tid {
238            (*f).lockcount += 1;
239            return;
240        }
241        if v == 0 && crate::a_cas(&raw mut (*f).lock, 0, tid) == 0 {
242            (*f).lockcount = 1;
243            return;
244        }
245        if v != 0 {
246            crate::futex_wait(&raw mut (*f).lock, v);
247        }
248    }
249}
250
251#[no_mangle]
252pub unsafe extern "C" fn funlockfile(f: *mut crate::FILE) {
253    if (*f).lock < 0 { return; }
254    if (*f).lockcount > 1 {
255        (*f).lockcount -= 1;
256        return;
257    }
258    (*f).lockcount = 0;
259    crate::a_store(&raw mut (*f).lock, 0);
260    crate::futex_wake(&raw mut (*f).lock, 1);
261}
262
263#[no_mangle]
264pub unsafe extern "C" fn __libc_current_sigrtmin() -> c_int {
265    34
266}
267
268#[no_mangle]
269pub unsafe extern "C" fn strdup(s: *const c_char) -> *mut c_char {
270    if s.is_null() {
271        return core::ptr::null_mut();
272    }
273    let len = crate::strlen(s);
274    let p = crate::malloc(len + 1) as *mut c_char;
275    if p.is_null() {
276        return core::ptr::null_mut();
277    }
278    crate::memcpy(p as *mut c_void, s as *const c_void, len + 1);
279    p
280}
281
282static mut MKTEMP_COUNTER: c_uint = 0;
283
284unsafe fn mktemp_internal(
285    template: *mut c_char,
286    mkdir_fn: Option<unsafe extern "C" fn(*const c_char, c_uint) -> c_int>,
287) -> *mut c_char {
288    if template.is_null() {
289        crate::__errno_location().write(EINVAL);
290        return core::ptr::null_mut();
291    }
292    let len = crate::strlen(template);
293    if len < 6 {
294        crate::__errno_location().write(EINVAL);
295        return core::ptr::null_mut();
296    }
297    let mut xcount = 0usize;
298    while xcount < 6 {
299        let ch = *template.add(len - 1 - xcount) as u8;
300        if ch != b'X' {
301            break;
302        }
303        xcount += 1;
304    }
305    if xcount != 6 {
306        crate::__errno_location().write(EINVAL);
307        return core::ptr::null_mut();
308    }
309    let _pid = crate::getpid();
310    let mut c = MKTEMP_COUNTER;
311    for _ in 0..1000 {
312        let mut n = c;
313        for i in 0..6 {
314            let digit = (n % 36) as u8;
315            let ch = if digit < 10 { b'0' + digit } else { b'a' + digit - 10 };
316            *template.add(len - 6 + i) = ch as c_char;
317            n /= 36;
318        }
319        c = c.wrapping_add(1);
320        if mkdir_fn.is_some() {
321            if crate::mkdir(template, 0o700) == 0 {
322                MKTEMP_COUNTER = c;
323                return template;
324            }
325            let e = crate::__errno_location().read();
326            if e != EEXIST && e != EINTR {
327                break;
328            }
329        } else {
330            MKTEMP_COUNTER = c;
331            return template;
332        }
333    }
334    MKTEMP_COUNTER = c;
335    template
336}
337
338#[no_mangle]
339pub unsafe extern "C" fn mkdtemp(template: *mut c_char) -> *mut c_char {
340    mktemp_internal(template, Some(crate::mkdir))
341}
342
343#[no_mangle]
344pub unsafe extern "C" fn mktemp(template: *mut c_char) -> *mut c_char {
345    mktemp_internal(template, None)
346}