Compare commits
55
Commits
6854c7771d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d07a914a1
|
||
|
|
49968bdbfc
|
||
|
|
1f39e217d4
|
||
|
|
b731a02db4
|
||
|
|
c4c64a4eb5
|
||
|
|
9eb32c4010
|
||
|
|
523045b5ae
|
||
|
|
9b576757cf
|
||
|
|
fefd17241f
|
||
|
|
3bf875030a
|
||
|
|
f9a055f547
|
||
|
|
9ffe86fc55
|
||
|
|
8c0f23fbd0
|
||
|
|
e9aa002453
|
||
|
|
3ab43c7f10
|
||
|
|
c4156efeff
|
||
|
|
ea0482e9ff
|
||
|
|
c67e890b17
|
||
|
|
d495d3eb89
|
||
|
|
ed996ff4c4
|
||
|
|
3eff4c97fd
|
||
|
|
ddcf8f39be
|
||
|
|
5af4197ee9
|
||
|
|
a2f7c33601
|
||
|
|
14c6fd6cf6
|
||
|
|
c9e9676d38
|
||
|
|
6eaacd4448
|
||
|
|
98f4c3169c
|
||
|
|
87ab610a9b
|
||
|
|
7b11452b87
|
||
|
|
34408380f0
|
||
|
|
22587bd545
|
||
|
|
5355574121
|
||
|
|
b53ccaa1e8
|
||
|
|
bff8fe9c86
|
||
|
|
99eff26467
|
||
|
|
8bf754419f
|
||
|
|
14b0136a8e
|
||
|
|
26786e9027
|
||
|
|
bcb4e259c3
|
||
|
|
ce92aa2b6d
|
||
|
|
b2fbde0fc2
|
||
|
|
5eb3cbe302
|
||
|
|
06d2ec7c34
|
||
|
|
20f514d91c
|
||
|
|
858fc1762c
|
||
|
|
40a392f454
|
||
|
|
558cf8fd12
|
||
|
|
2480c63fbd
|
||
|
|
601e1fe960
|
||
|
|
e3017cfe56
|
||
|
|
b2d588885e
|
||
|
|
dec1017527
|
||
|
|
ce517ad0d6
|
||
|
|
c5a77c61fa
|
+11
@@ -107,3 +107,14 @@ Makefile.in
|
||||
/benchmarks/bench_vlibc
|
||||
/benchmarks/bench_string
|
||||
|
||||
# make check outputs: static test binaries + parallel-test logs (todo 6).
|
||||
/test_*
|
||||
/test-suite.log
|
||||
/tests/startup_modes.sh.log
|
||||
/tests/startup_modes.sh.trs
|
||||
|
||||
# make dist outputs.
|
||||
/vlibc-*.tar.gz
|
||||
/vlibc-*/
|
||||
|
||||
|
||||
|
||||
+652
-6
@@ -2,23 +2,333 @@
|
||||
|
||||
SUBDIRS = benchmarks
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
# Build-dir include FIRST: configure generates include/vlibc/features.h
|
||||
# (from the .in template) into the BUILD tree, and a VPATH build must find
|
||||
# the generated header before the source tree's checked-in headers.
|
||||
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
||||
AM_CFLAGS = @VLIBC_CFLAGS@
|
||||
|
||||
# ---- Library -------------------------------------------------------------
|
||||
vlibc_include_HEADERS = include/vlibc.h include/stddef.h include/string.h
|
||||
|
||||
# Authoritative source-dir inventory (todo 6). Every directory a later plan
|
||||
# todo creates is enumerated here ONCE, so no area can be forgotten; each is
|
||||
# appended to libvlibc_la_SOURCES WHEN ITS TODO LANDS — the list grows
|
||||
# incrementally and never references a directory that does not exist yet:
|
||||
#
|
||||
# Wired so far (W1-W3 + W5/W6 slices, each wired when its todo landed):
|
||||
# src/internal, src/start, src/errno,
|
||||
# src/setjmp/x86_64, src/string, crt/ (crt1.o),
|
||||
# src/malloc (todo 7), src/ctype (todo 9,51),
|
||||
# src/stdlib (todo 11-14, incl. the 13 env slice),
|
||||
# src/unistd (todo 19), src/process (todo 20,23),
|
||||
# src/fcntl (todo 21), src/stat (todo 22),
|
||||
# src/dirent (todo 24), src/select (todo 25),
|
||||
# src/time (todo 26 + todo 27's sys/time
|
||||
# slice — gettimeofday/settimeofday/
|
||||
# getitimer/setitimer/utimes.c + sys/time.h —
|
||||
# all wired), src/stdio (todo 15-18 FILE core
|
||||
# + popen; vfprintf.c/vfprintf_float.c now
|
||||
# wired with the printf test),
|
||||
# src/math (todo 39-42 basic set — all L1,
|
||||
# wired with the consolidated math test)
|
||||
# todo 39-42 src/math todo 50 src/multibyte
|
||||
# todo 11-14 src/stdlib todo 43 src/complex todo 53-54 src/regex
|
||||
# todo 15-18 src/stdio todo 9,51 src/ctype todo 57-58 src/network
|
||||
# todo 19,38 src/unistd todo 55 src/search todo 59 src/mq
|
||||
# todo 20,23,56 src/process todo 66 src/crypt todo 60 src/aio
|
||||
# todo 21 src/fcntl todo 44-48 src/thread todo 61 src/ipc
|
||||
# todo 22,34 src/stat todo 49,52 src/locale todo 62-63 src/ldso
|
||||
# todo 24 src/dirent todo 42 src/fenv/x86_64
|
||||
# todo 25 src/select
|
||||
# todo 26-27 src/time
|
||||
# todo 28 src/signal
|
||||
# todo 29 src/termios
|
||||
# todo 30 src/uio
|
||||
# todo 31 src/resource
|
||||
# todo 32,36,54,61,67 src/misc
|
||||
# todo 33 src/mman
|
||||
# todo 35 src/sys
|
||||
# todo 37 src/passwd
|
||||
# todo 37 src/group
|
||||
#
|
||||
# NOTE: crt/x86_64/crt1.s is NOT a library source — it is assembled into the
|
||||
# standalone crt1.o below (the sole crt owner; todo 64 reuses, never rebuilds).
|
||||
|
||||
# Level-independent L1 core (wired today + the pre-plan string slice).
|
||||
# W5/W6 additions follow the established split rule: whole-content-L2 TUs
|
||||
# (lockf, mknod, readdir_r/scandir/alphasort, ppoll, strptime/timer) go to
|
||||
# VLIBC_LEVEL2_SRCS; L1 bodies with L2 sections self-gated inside the .c
|
||||
# (fork.c vfork, groups.c setgroups, session.c tail, wait.c wait3/wait4,
|
||||
# time ctime/mktime/nanosleep/strftime/tzset L2 tails, stdio.c L2 tail,
|
||||
# unistd lseek64/dup3/pipe2/truncate) stay in CORE. task-19's unistd dir is
|
||||
# CORE throughout (self-gated L2 sections, no whole-file gate). T27's
|
||||
# sys/time slice (gettimeofday/settimeofday/getitimer/setitimer/utimes) is
|
||||
# whole-file L2, so it lives in VLIBC_LEVEL2_SRCS.
|
||||
VLIBC_CORE_SRCS = \
|
||||
src/vlibc.c \
|
||||
src/internal/assert_fail.c \
|
||||
src/internal/errno.c \
|
||||
src/internal/syscall_ret.c \
|
||||
src/start/libc_start_main.c \
|
||||
src/start/exit.c \
|
||||
src/start/environ.c \
|
||||
src/start/tls.c \
|
||||
src/thread/futex.c \
|
||||
src/thread/pthread_self.c \
|
||||
src/thread/tls.c \
|
||||
src/thread/x86_64/__set_thread_area.s \
|
||||
src/errno/strerror.c \
|
||||
src/setjmp/x86_64/setjmp.s \
|
||||
src/setjmp/x86_64/longjmp.s \
|
||||
src/setjmp/x86_64/sigsetjmp.s \
|
||||
src/setjmp/x86_64/siglongjmp.s \
|
||||
src/string/strlen.c \
|
||||
src/string/strcmp.c \
|
||||
src/string/memcpy.c \
|
||||
src/string/memmove.c \
|
||||
src/string/memset.c \
|
||||
src/string/strcat.c \
|
||||
src/string/strchr.c \
|
||||
src/string/strcoll.c \
|
||||
src/string/strcpy.c \
|
||||
src/string/strdup.c \
|
||||
src/string/memchr.c \
|
||||
src/string/memcmp.c \
|
||||
src/string/strncmp.c \
|
||||
src/string/strncpy.c \
|
||||
src/string/strnlen.c \
|
||||
src/string/strsignal.c \
|
||||
src/string/strspn.c \
|
||||
src/string/strstr.c \
|
||||
src/string/strtok.c \
|
||||
src/malloc/malloc.c \
|
||||
src/ctype/ctype.c \
|
||||
src/stdlib/strtox.c \
|
||||
src/stdlib/strtox_impl.c \
|
||||
src/stdlib/strtod.c \
|
||||
src/stdlib/rand.c \
|
||||
src/stdlib/qsort.c \
|
||||
src/stdlib/div.c \
|
||||
src/stdlib/inttypes.c \
|
||||
src/stdlib/env.c \
|
||||
src/stdlib/multibyte.c \
|
||||
src/stdlib/mkstemp.c \
|
||||
src/stdio/stdio.c \
|
||||
src/stdio/fmemopen.c \
|
||||
src/stdio/getline.c \
|
||||
src/stdio/locking.c \
|
||||
src/stdio/popen.c \
|
||||
src/stdio/vfscanf.c \
|
||||
src/stdio/vfprintf.c \
|
||||
src/unistd/access.c \
|
||||
src/unistd/close.c \
|
||||
src/unistd/dup.c \
|
||||
src/unistd/fsync.c \
|
||||
src/unistd/lseek.c \
|
||||
src/unistd/open.c \
|
||||
src/unistd/pipe.c \
|
||||
src/unistd/pread.c \
|
||||
src/unistd/rw.c \
|
||||
src/unistd/sync.c \
|
||||
src/unistd/truncate.c \
|
||||
src/unistd/chdir.c \
|
||||
src/unistd/getcwd.c \
|
||||
src/unistd/getlogin.c \
|
||||
src/unistd/isatty.c \
|
||||
src/unistd/link.c \
|
||||
src/unistd/readlink.c \
|
||||
src/unistd/renameat.c \
|
||||
src/unistd/rmdir.c \
|
||||
src/unistd/symlink.c \
|
||||
src/unistd/ttyname.c \
|
||||
src/unistd/unlink.c \
|
||||
src/unistd/getopt.c \
|
||||
src/process/atfork.c \
|
||||
src/process/execl.c \
|
||||
src/process/execle.c \
|
||||
src/process/execlp.c \
|
||||
src/process/execv.c \
|
||||
src/process/execve.c \
|
||||
src/process/execvp.c \
|
||||
src/process/fork.c \
|
||||
src/process/groups.c \
|
||||
src/process/pid.c \
|
||||
src/process/session.c \
|
||||
src/process/system.c \
|
||||
src/process/uid.c \
|
||||
src/process/wait.c \
|
||||
src/fcntl/creat.c \
|
||||
src/fcntl/fcntl.c \
|
||||
src/fcntl/posix_fadvise.c \
|
||||
src/fcntl/posix_fallocate.c \
|
||||
src/stat/chmod.c \
|
||||
src/stat/chown.c \
|
||||
src/stat/fstatat.c \
|
||||
src/stat/mkdir.c \
|
||||
src/stat/mkfifo.c \
|
||||
src/stat/umask.c \
|
||||
src/stat/utimensat.c \
|
||||
src/dirent/closedir.c \
|
||||
src/dirent/dirfd.c \
|
||||
src/dirent/fdopendir.c \
|
||||
src/dirent/opendir.c \
|
||||
src/dirent/readdir.c \
|
||||
src/dirent/rewinddir.c \
|
||||
src/dirent/seekdir.c \
|
||||
src/dirent/telldir.c \
|
||||
src/select/poll.c \
|
||||
src/select/pselect.c \
|
||||
src/select/select.c \
|
||||
src/time/time.c \
|
||||
src/time/clock.c \
|
||||
src/time/timespec_get.c \
|
||||
src/time/sleep.c \
|
||||
src/time/nanosleep.c \
|
||||
src/time/mktime.c \
|
||||
src/time/ctime.c \
|
||||
src/time/tzset.c \
|
||||
src/time/strftime.c \
|
||||
src/mman/mmap.c \
|
||||
src/passwd/passwd.c \
|
||||
src/group/group.c \
|
||||
src/math/cbrt.c \
|
||||
src/math/ceil.c \
|
||||
src/math/copysign.c \
|
||||
src/math/exp.c \
|
||||
src/math/fabs.c \
|
||||
src/math/fdim.c \
|
||||
src/math/floor.c \
|
||||
src/math/fmax.c \
|
||||
src/math/fmin.c \
|
||||
src/math/fmod.c \
|
||||
src/math/frexp.c \
|
||||
src/math/hypot.c \
|
||||
src/math/ilogb.c \
|
||||
src/math/ldexp.c \
|
||||
src/math/llrint.c \
|
||||
src/math/llround.c \
|
||||
src/math/log.c \
|
||||
src/math/log1p.c \
|
||||
src/math/logb.c \
|
||||
src/math/lrint.c \
|
||||
src/math/lround.c \
|
||||
src/math/modf.c \
|
||||
src/math/nearbyint.c \
|
||||
src/math/pow.c \
|
||||
src/math/remainder.c \
|
||||
src/math/remquo.c \
|
||||
src/math/rint.c \
|
||||
src/math/round.c \
|
||||
src/math/scalbn.c \
|
||||
src/math/sqrt.c \
|
||||
src/math/trunc.c
|
||||
|
||||
# Profile-gated sources, EXISTING gates preserved (strlcpy/strlcat stay L2
|
||||
# BSD, strcasestr stays L3 GNU — NOT promoted). Task 8/9 additions: stpcpy.c
|
||||
# (stpcpy/stpncpy) and memccpy.c are WHOLE-FILE L2-gated (#if VLIBC_LEVEL_GE(2)
|
||||
# around the entire body), and isascii.c/toascii.c hold only L2 symbols
|
||||
# (the header gates their declarations) — all four must be omitted from a
|
||||
# level-1 profile, so they join the GE_2 conditional list. Task 10's
|
||||
# strings_impl.c is likewise whole-file L2-gated (strcasecmp/strncasecmp/ffs/
|
||||
# ffsl/ffsll/bcmp/bcopy/bzero/index/rindex — nothing is POSIX.1-2008 base), so
|
||||
# it also joins the GE_2 conditional list. Files with L1
|
||||
# bodies plus small L2 sections (strcoll.c, strdup.c) stay in VLIBC_CORE_SRCS
|
||||
# and self-gate at level 1. Likewise rand.c (its L2 rand_r/drand48 section)
|
||||
# and the strtox/strtod/inttypes/div/qsort C files (their L2 gates live in
|
||||
# include/stdlib.h / include/inttypes.h declarations) stay in VLIBC_CORE_SRCS.
|
||||
# W5/W6 whole-file-L2 joins (verified by reading each .c — the #if
|
||||
# VLIBC_LEVEL_GE(2) wraps the whole body after the includes): lockf.c (XSI),
|
||||
# mknod.c (XSI), readdir_r.c/scandir.c/alphasort.c (XSI dirent), ppoll.c
|
||||
# (Linux), strptime.c/timer.c (POSIX timers), plus task 27's sys/time slice
|
||||
# (gettimeofday.c/settimeofday.c/getitimer.c/setitimer.c/utimes.c — each
|
||||
# whole-file L2, wired with the systime test), plus the earlier strings
|
||||
# slice.
|
||||
VLIBC_LEVEL2_SRCS = src/string/strlcpy.c src/string/strlcat.c \
|
||||
src/string/stpcpy.c src/string/memccpy.c \
|
||||
src/ctype/isascii.c src/ctype/toascii.c \
|
||||
src/string/strings_impl.c \
|
||||
src/stdlib/pty.c src/stdlib/getsubopt.c \
|
||||
src/fcntl/lockf.c \
|
||||
src/stat/mknod.c \
|
||||
src/dirent/readdir_r.c src/dirent/scandir.c src/dirent/alphasort.c \
|
||||
src/select/ppoll.c \
|
||||
src/time/strptime.c src/time/timer.c \
|
||||
src/time/gettimeofday.c src/time/settimeofday.c \
|
||||
src/time/getitimer.c src/time/setitimer.c src/time/utimes.c \
|
||||
src/stat/statvfs.c src/sys/times.c src/misc/syslog.c \
|
||||
src/unistd/basename.c src/unistd/dirname.c src/unistd/realpath.c \
|
||||
src/unistd/chroot.c src/unistd/swab.c src/unistd/confstr.c \
|
||||
src/unistd/getwd.c
|
||||
VLIBC_LEVEL3_SRCS = src/string/strcasestr.c
|
||||
|
||||
vlibc_lib_LTLIBRARIES = libvlibc.la
|
||||
|
||||
libvlibc_la_SOURCES = src/vlibc.c src/string/strlen.c src/string/strcmp.c \
|
||||
src/string/memcpy.c src/string/memmove.c src/string/memset.c
|
||||
libvlibc_la_SOURCES = $(VLIBC_CORE_SRCS)
|
||||
if PROFILE_GE_2
|
||||
libvlibc_la_SOURCES += src/string/strlcpy.c src/string/strlcat.c
|
||||
libvlibc_la_SOURCES += $(VLIBC_LEVEL2_SRCS)
|
||||
endif
|
||||
if PROFILE_GE_3
|
||||
libvlibc_la_SOURCES += src/string/strcasestr.c
|
||||
libvlibc_la_SOURCES += $(VLIBC_LEVEL3_SRCS)
|
||||
endif
|
||||
libvlibc_la_LDFLAGS = -version-info 0:0:0 -no-undefined
|
||||
|
||||
# ---- Public headers ------------------------------------------------------
|
||||
# The whole public header inventory (the 3 scaffold headers plus todo 5's
|
||||
# skeleton); the profile-gated generated features.h is installed separately
|
||||
# below.
|
||||
vlibc_include_HEADERS = \
|
||||
include/vlibc.h \
|
||||
include/stddef.h \
|
||||
include/string.h \
|
||||
include/strings.h \
|
||||
include/errno.h \
|
||||
include/setjmp.h \
|
||||
include/assert.h \
|
||||
include/ctype.h \
|
||||
include/cpio.h \
|
||||
include/float.h \
|
||||
include/iso646.h \
|
||||
include/limits.h \
|
||||
include/stdalign.h \
|
||||
include/stdarg.h \
|
||||
include/stdatomic.h \
|
||||
include/stdbit.h \
|
||||
include/stdbool.h \
|
||||
include/stdckdint.h \
|
||||
include/stdint.h \
|
||||
include/inttypes.h \
|
||||
include/stdlib.h \
|
||||
include/stdnoreturn.h \
|
||||
include/tar.h \
|
||||
include/math.h \
|
||||
include/tgmath.h \
|
||||
include/threads.h \
|
||||
include/uchar.h \
|
||||
include/dirent.h \
|
||||
include/fcntl.h \
|
||||
include/poll.h \
|
||||
include/stdio.h \
|
||||
include/time.h \
|
||||
include/unistd.h \
|
||||
include/syslog.h \
|
||||
include/pwd.h \
|
||||
include/grp.h \
|
||||
include/libgen.h
|
||||
|
||||
# sys/* headers use their own primary so the sys/ subpath survives install:
|
||||
# automake's flat _HEADERS install strips directory components, which would
|
||||
# both drop the sys/ prefix for <sys/*.h> consumers AND collide on shared
|
||||
# basenames (time.h vs sys/time.h, GNU install refuses the second copy).
|
||||
vlibc_include_sys_HEADERS = \
|
||||
include/sys/select.h \
|
||||
include/sys/stat.h \
|
||||
include/sys/time.h \
|
||||
include/sys/types.h \
|
||||
include/sys/wait.h \
|
||||
include/sys/mman.h \
|
||||
include/sys/statvfs.h \
|
||||
include/sys/times.h
|
||||
|
||||
# Install location depends on the install method (see configure.ac).
|
||||
if INSTALL_OVERWRITE
|
||||
vlibc_includedir = $(includedir)
|
||||
@@ -28,6 +338,11 @@ vlibc_includedir = $(prefix)/lib/vlibc/include
|
||||
vlibc_libdir = $(prefix)/lib/vlibc/lib
|
||||
endif
|
||||
|
||||
# sys/* headers live under $(vlibc_includedir)/sys (see the sys primary
|
||||
# above; must follow the INSTALL_OVERWRITE conditional to inherit the
|
||||
# resolved vlibc_includedir).
|
||||
vlibc_include_sysdir = $(vlibc_includedir)/sys
|
||||
|
||||
# Generated per-profile feature header (see configure.ac); installed under
|
||||
# $(vlibc_includedir)/vlibc/. A dedicated primary is used because
|
||||
# nodist_nobase_vlibc_include_HEADERS is rejected by automake (the "nobase"
|
||||
@@ -38,6 +353,17 @@ nodist_vlibc_features_HEADERS = include/vlibc/features.h
|
||||
# ---- Compiler drivers ----------------------------------------------------
|
||||
bin_SCRIPTS = tools/vlibc-gcc tools/vlibc-clang
|
||||
|
||||
# ---- crt objects ---------------------------------------------------------
|
||||
# crt1.o (from crt/x86_64/crt1.s) is built here and ONLY here: todo 64
|
||||
# reuses these objects for the shared build and must NOT rebuild them.
|
||||
# Built by `make all` via all-local so a static vlibc link always has its
|
||||
# entry object available next to the archive.
|
||||
|
||||
crt1.o: crt/x86_64/crt1.s
|
||||
$(AM_V_GEN)$(CC) $(AM_CPPFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
all-local: crt1.o
|
||||
|
||||
# ---- Targets -------------------------------------------------------------
|
||||
# layouts/C.md requires `make debug`, `make release`, `make bench` and
|
||||
# `make clean`; build outputs land in bin/{release,debug}.
|
||||
@@ -50,6 +376,7 @@ debug:
|
||||
$(MKDIR_P) bin/debug
|
||||
-cp -P .libs/libvlibc.so* bin/debug/
|
||||
-cp .libs/libvlibc.a bin/debug/
|
||||
-cp crt1.o bin/debug/
|
||||
|
||||
release:
|
||||
$(MAKE) clean
|
||||
@@ -57,6 +384,7 @@ release:
|
||||
$(MKDIR_P) bin/release
|
||||
-cp -P .libs/libvlibc.so* bin/release/
|
||||
-cp .libs/libvlibc.a bin/release/
|
||||
-cp crt1.o bin/release/
|
||||
|
||||
bench: all
|
||||
$(MAKE) -C benchmarks bench
|
||||
@@ -68,3 +396,321 @@ compile-commands: clean
|
||||
bear --append -- $(MAKE) -C benchmarks bench_vlibc
|
||||
|
||||
.PHONY: debug release bench compile-commands
|
||||
|
||||
# ---- make check ----------------------------------------------------------
|
||||
#
|
||||
# Every test binary is a STATIC VLIBC EXECUTABLE: linked with -nostdlib
|
||||
# -static -no-pie against crt1.o + a dedicated level-2 test archive. No
|
||||
# glibc symbol can resolve, so each test genuinely proves self-hosting.
|
||||
#
|
||||
# The test archive (libvlibc-check.a) is compiled from the FULL source list
|
||||
# (all levels included) at VLIBC_LEVEL=2 — the complete L1+L2 surface —
|
||||
# INDEPENDENT of the configured profile. Rationale: some tests exercise
|
||||
# L2-gated functions (test_strerror calls strerror_r, future tests call
|
||||
# bcopy etc.), which a level-1 configured archive does not provide; the
|
||||
# shipped libvlibc.a keeps the profile gating configured at build time.
|
||||
# Object files are plain `ar` members compiled directly with $(CC) — no
|
||||
# libtool (libtool's build-dir libvlibc.a is a wrapper script, and only
|
||||
# .libs/libvlibc.a is linkable, which is a level-gated profile archive).
|
||||
#
|
||||
# Mechanics (extend here for future tests):
|
||||
# - Add the binary to check_PROGRAMS + TESTS with
|
||||
# `<bin>_SOURCES = tests/test_x.c`,
|
||||
# `<bin>_CFLAGS = $(VLIBC_TEST_CFLAGS)` (renames the object to
|
||||
# `<bin>-<src>.o` automatically),
|
||||
# `<bin>_LINK = $(VLIBC_TEST_LINK)` and
|
||||
# `<bin>_LDADD = $(VLIBC_TEST_LDADD)` (static vlibc link; the crt
|
||||
# object + archive must be in LDADD so they follow the test object).
|
||||
# - Multi-mode tests run through tests/startup_modes.sh (see that file).
|
||||
|
||||
VLIBC_TEST_LEVEL = 2
|
||||
|
||||
VLIBC_CHECK_SRCS = $(VLIBC_CORE_SRCS) $(VLIBC_LEVEL2_SRCS) \
|
||||
$(VLIBC_LEVEL3_SRCS)
|
||||
VLIBC_CHECK_OBJECTS_FROM_C = $(VLIBC_CHECK_SRCS:.c=.check.o)
|
||||
VLIBC_CHECK_OBJECTS = $(VLIBC_CHECK_OBJECTS_FROM_C:.s=.check.o)
|
||||
VLIBC_CHECK_CFLAGS = @VLIBC_CFLAGS@ -DVLIBC_LEVEL=$(VLIBC_TEST_LEVEL) -fno-pic
|
||||
|
||||
# The archive itself (plain ar: members compiled at the test level).
|
||||
libvlibc-check.a: $(VLIBC_CHECK_OBJECTS)
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
# Library sources compiled at the test level. Deliberately no
|
||||
# -DHAVE_CONFIG_H and no $(DEFS): config.h pins the PROFILE level, which
|
||||
# would override -DVLIBC_LEVEL (last definition wins), so the check objects
|
||||
# must get their level from <vlibc/features.h> alone.
|
||||
%.check.o: %.c
|
||||
$(AM_V_CC)$(CC) $(AM_CPPFLAGS) $(CPPFLAGS) $(VLIBC_CHECK_CFLAGS) -c -o $@ $<
|
||||
|
||||
%.check.o: %.s
|
||||
$(AM_V_CCAS)$(CC) $(AM_CPPFLAGS) $(CPPFLAGS) -c -o $@ $<
|
||||
|
||||
# Static vlibc link for every check program. automake links
|
||||
# "$(<bin>_LINK) $(<bin>_OBJECTS) $(<bin>_LDADD) $(LIBS)", so the entry
|
||||
# object, the level-2 archive and libgcc go in LDADD — they MUST follow the
|
||||
# test object or the archive would see no undefined symbols and stay
|
||||
# unextracted. -nostdlib means -lgcc is not auto-added, so it is spelled
|
||||
# out (permitted libgcc helpers).
|
||||
VLIBC_TEST_LINK = $(CC) -nostdlib -static -no-pie -o $@
|
||||
VLIBC_TEST_LDADD = crt1.o libvlibc-check.a -lgcc
|
||||
|
||||
# Test TUs: no stack protector (the library is protector-free at this stage),
|
||||
# non-PIC, level overridden to the test level so L2 declarations are visible.
|
||||
VLIBC_TEST_CFLAGS = -DVLIBC_LEVEL=$(VLIBC_TEST_LEVEL) -fno-stack-protector \
|
||||
-fno-pic -fno-pie
|
||||
|
||||
check_PROGRAMS = test_syscall test_strerror test_setjmp test_headers \
|
||||
test_startup test_malloc test_string test_ctype test_strings \
|
||||
test_strtol test_qsort test_inttypes test_env \
|
||||
test_process test_fcntl test_stat test_wait test_dirent \
|
||||
test_poll test_time test_scanf test_getline \
|
||||
test_systime test_printf test_stdio test_unistd_file \
|
||||
test_mman test_statvfs test_times test_syslog test_pwdgrp \
|
||||
test_unistd_misc test_math_basic
|
||||
|
||||
test_syscall_SOURCES = tests/syscall_test.c
|
||||
test_syscall_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_syscall_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_syscall_LINK = $(VLIBC_TEST_LINK)
|
||||
test_syscall_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_strerror_SOURCES = tests/test_strerror.c
|
||||
test_strerror_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_strerror_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_strerror_LINK = $(VLIBC_TEST_LINK)
|
||||
test_strerror_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_setjmp_SOURCES = tests/test_setjmp.c
|
||||
test_setjmp_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_setjmp_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_setjmp_LINK = $(VLIBC_TEST_LINK)
|
||||
test_setjmp_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_headers_SOURCES = tests/test_headers.c
|
||||
test_headers_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_headers_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_headers_LINK = $(VLIBC_TEST_LINK)
|
||||
test_headers_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_startup_SOURCES = tests/test_startup.c
|
||||
test_startup_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_startup_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_startup_LINK = $(VLIBC_TEST_LINK)
|
||||
test_startup_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_malloc_SOURCES = tests/test_malloc.c
|
||||
test_malloc_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_malloc_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_malloc_LINK = $(VLIBC_TEST_LINK)
|
||||
test_malloc_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_string_SOURCES = tests/test_string.c
|
||||
test_string_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_string_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_string_LINK = $(VLIBC_TEST_LINK)
|
||||
test_string_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_ctype_SOURCES = tests/test_ctype.c
|
||||
test_ctype_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_ctype_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_ctype_LINK = $(VLIBC_TEST_LINK)
|
||||
test_ctype_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_strings_SOURCES = tests/test_strings.c
|
||||
test_strings_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_strings_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_strings_LINK = $(VLIBC_TEST_LINK)
|
||||
test_strings_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_strtol_SOURCES = tests/test_strtol.c
|
||||
test_strtol_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_strtol_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_strtol_LINK = $(VLIBC_TEST_LINK)
|
||||
test_strtol_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_qsort_SOURCES = tests/test_qsort.c
|
||||
test_qsort_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_qsort_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_qsort_LINK = $(VLIBC_TEST_LINK)
|
||||
test_qsort_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_inttypes_SOURCES = tests/test_inttypes.c
|
||||
test_inttypes_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_inttypes_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_inttypes_LINK = $(VLIBC_TEST_LINK)
|
||||
test_inttypes_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_env_SOURCES = tests/test_env.c
|
||||
test_env_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_env_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_env_LINK = $(VLIBC_TEST_LINK)
|
||||
test_env_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_process_SOURCES = tests/test_process.c
|
||||
test_process_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_process_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_process_LINK = $(VLIBC_TEST_LINK)
|
||||
test_process_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_fcntl_SOURCES = tests/test_fcntl.c
|
||||
test_fcntl_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_fcntl_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_fcntl_LINK = $(VLIBC_TEST_LINK)
|
||||
test_fcntl_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_stat_SOURCES = tests/test_stat.c
|
||||
test_stat_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_stat_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_stat_LINK = $(VLIBC_TEST_LINK)
|
||||
test_stat_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_wait_SOURCES = tests/test_wait.c
|
||||
test_wait_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_wait_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_wait_LINK = $(VLIBC_TEST_LINK)
|
||||
test_wait_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_dirent_SOURCES = tests/test_dirent.c
|
||||
test_dirent_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_dirent_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_dirent_LINK = $(VLIBC_TEST_LINK)
|
||||
test_dirent_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_poll_SOURCES = tests/test_poll.c
|
||||
test_poll_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_poll_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_poll_LINK = $(VLIBC_TEST_LINK)
|
||||
test_poll_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_time_SOURCES = tests/test_time.c
|
||||
test_time_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_time_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_time_LINK = $(VLIBC_TEST_LINK)
|
||||
test_time_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_scanf_SOURCES = tests/test_scanf.c
|
||||
test_scanf_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_scanf_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_scanf_LINK = $(VLIBC_TEST_LINK)
|
||||
test_scanf_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_getline_SOURCES = tests/test_getline.c
|
||||
test_getline_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_getline_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_getline_LINK = $(VLIBC_TEST_LINK)
|
||||
test_getline_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_systime_SOURCES = tests/test_systime.c
|
||||
test_systime_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_systime_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_systime_LINK = $(VLIBC_TEST_LINK)
|
||||
test_systime_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_printf_SOURCES = tests/test_printf.c
|
||||
test_printf_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_printf_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_printf_LINK = $(VLIBC_TEST_LINK)
|
||||
test_printf_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_stdio_SOURCES = tests/test_stdio.c
|
||||
test_stdio_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_stdio_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_stdio_LINK = $(VLIBC_TEST_LINK)
|
||||
test_stdio_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_unistd_file_SOURCES = tests/test_unistd_file.c
|
||||
test_unistd_file_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_unistd_file_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_unistd_file_LINK = $(VLIBC_TEST_LINK)
|
||||
test_unistd_file_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_mman_SOURCES = tests/test_mman.c
|
||||
test_mman_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_mman_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_mman_LINK = $(VLIBC_TEST_LINK)
|
||||
test_mman_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_statvfs_SOURCES = tests/test_statvfs.c
|
||||
test_statvfs_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_statvfs_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_statvfs_LINK = $(VLIBC_TEST_LINK)
|
||||
test_statvfs_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_times_SOURCES = tests/test_times.c
|
||||
test_times_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_times_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_times_LINK = $(VLIBC_TEST_LINK)
|
||||
test_times_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_syslog_SOURCES = tests/test_syslog.c
|
||||
test_syslog_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_syslog_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_syslog_LINK = $(VLIBC_TEST_LINK)
|
||||
test_syslog_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_pwdgrp_SOURCES = tests/test_pwdgrp.c
|
||||
test_pwdgrp_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_pwdgrp_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_pwdgrp_LINK = $(VLIBC_TEST_LINK)
|
||||
test_pwdgrp_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_unistd_misc_SOURCES = tests/test_unistd_misc.c
|
||||
test_unistd_misc_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_unistd_misc_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_unistd_misc_LINK = $(VLIBC_TEST_LINK)
|
||||
test_unistd_misc_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
test_math_basic_SOURCES = tests/test_math_basic.c
|
||||
test_math_basic_CFLAGS = $(VLIBC_TEST_CFLAGS)
|
||||
test_math_basic_DEPENDENCIES = crt1.o libvlibc-check.a
|
||||
test_math_basic_LINK = $(VLIBC_TEST_LINK)
|
||||
test_math_basic_LDADD = $(VLIBC_TEST_LDADD)
|
||||
|
||||
# test_startup's default mode exits 42 by design, so it is NOT a bare TESTS
|
||||
# entry; tests/startup_modes.sh runs its full mode matrix and the -f failure
|
||||
# scenarios of the other binaries. test_malloc/test_string/test_ctype/
|
||||
# test_strings/test_strtol/test_qsort/test_inttypes/test_env and the W5/W6
|
||||
# wave tests (test_process/test_fcntl/test_stat/test_wait/test_dirent/
|
||||
# test_poll/test_time/test_scanf/test_getline) default to the happy mode
|
||||
# (exit 0 on all-pass) and are bare TESTS entries; their -f/-p modes stay
|
||||
# runnable by hand.
|
||||
TESTS = test_syscall test_strerror test_setjmp test_headers \
|
||||
test_malloc test_string test_ctype test_strings \
|
||||
test_strtol test_qsort test_inttypes test_env \
|
||||
test_process test_fcntl test_stat test_wait test_dirent \
|
||||
test_poll test_time test_scanf test_getline \
|
||||
test_systime test_printf test_stdio test_unistd_file \
|
||||
test_mman test_statvfs test_times test_syslog test_pwdgrp \
|
||||
test_unistd_misc \
|
||||
test_math_basic \
|
||||
tests/startup_modes.sh
|
||||
|
||||
CLEANFILES = crt1.o libvlibc-check.a $(VLIBC_CHECK_OBJECTS)
|
||||
|
||||
# Distributed but not installed / not otherwise picked up by automake.
|
||||
# (TESTS scripts are not auto-distributed, so the mode runner is explicit.)
|
||||
EXTRA_DIST = \
|
||||
crt/x86_64/crt1.s \
|
||||
include/vlibc/internal/test.h \
|
||||
tests/conformance/symbols.list \
|
||||
tests/startup_modes.sh \
|
||||
arch/x86_64/syscall_arch.h \
|
||||
src/internal/atomic.h \
|
||||
src/internal/errno.h \
|
||||
src/internal/futex.h \
|
||||
src/internal/libc.h \
|
||||
src/internal/malloc.h \
|
||||
src/internal/strtox.h \
|
||||
src/internal/syscall.h \
|
||||
src/internal/types.h \
|
||||
src/start/start.h \
|
||||
src/start/tcb.h \
|
||||
src/thread/pthread_impl.h \
|
||||
src/dirent/dirent_impl.h \
|
||||
src/process/atfork_impl.h \
|
||||
src/stat/stat_impl.h \
|
||||
src/stdio/stdio_impl.h \
|
||||
src/stdio/vfprintf_float.c \
|
||||
src/time/time_impl.h \
|
||||
src/unistd/unistd_impl.h \
|
||||
src/math/math_impl.h
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#ifndef VLIBC_INTERNAL_ARCH_X86_64_SYSCALL_ARCH_H
|
||||
#define VLIBC_INTERNAL_ARCH_X86_64_SYSCALL_ARCH_H
|
||||
|
||||
/*
|
||||
* vlibc — x86_64 raw syscall entry (arch layer).
|
||||
*
|
||||
* The System V AMD64 kernel ABI: the syscall number goes in rax, arguments in
|
||||
* rdi, rsi, rdx, r10, r8, r9 (this order; the fourth argument is r10, NOT
|
||||
* rcx), and the kernel clobbers rcx and r11. The kernel returns the raw
|
||||
* result in rax: a non-negative value on success, or -errno on error. Callers
|
||||
* funnel rax through syscall_ret() (see src/internal/syscall.h), which
|
||||
* translates the negative-errno convention into C return/-1 + errno.
|
||||
*
|
||||
* These wrappers take `long` arguments and return `long` so that pointer-sized
|
||||
* syscall arguments (addresses, off_t) pass through without truncation; the
|
||||
* kernel reads exactly the low 64 bits of each register, so the upper bits of
|
||||
* `long` arguments are irrelevant.
|
||||
*
|
||||
* Each wrapper is `always_inline` so that even a -O0 build collapses to a
|
||||
* bare `syscall` instruction with no call frame. The "memory" clobber tells
|
||||
* the compiler the syscall may read or write anything the caller can see,
|
||||
* which is always the safe assumption.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The __syscall<n> names sit in the implementation-reserved namespace (they
|
||||
* are this libc's private kernel-ABI seam, never public API), and every
|
||||
* argument is a `long` because that is the shape of the x86_64 syscall
|
||||
* register file — both properties are intentional, so the corresponding
|
||||
* bugprone checks are waived per function.
|
||||
*/
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters,bugprone-reserved-identifier)
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall0(long n)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall1(long n, long a)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("syscall" : "=a"(ret) : "a"(n), "D"(a) : "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall2(long n, long a, long b)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("syscall" : "=a"(ret) : "a"(n), "D"(a), "S"(b) : "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall3(long n, long a, long b, long c)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
__asm__ volatile("syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(n), "D"(a), "S"(b), "d"(c)
|
||||
: "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall4(long n, long a, long b, long c, long d)
|
||||
{
|
||||
unsigned long ret;
|
||||
register long r10 __asm__("r10") = d;
|
||||
|
||||
__asm__ volatile("syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(r10)
|
||||
: "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall5(long n, long a, long b, long c, long d, long e)
|
||||
{
|
||||
unsigned long ret;
|
||||
register long r10 __asm__("r10") = d;
|
||||
register long r8 __asm__("r8") = e;
|
||||
|
||||
__asm__ volatile("syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(r10), "r"(r8)
|
||||
: "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
static inline long __attribute__((always_inline))
|
||||
__syscall6(long n, long a, long b, long c, long d, long e, long f)
|
||||
{
|
||||
unsigned long ret;
|
||||
register long r10 __asm__("r10") = d;
|
||||
register long r8 __asm__("r8") = e;
|
||||
register long r9 __asm__("r9") = f;
|
||||
|
||||
__asm__ volatile("syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(r10), "r"(r8), "r"(r9)
|
||||
: "rcx", "r11", "memory");
|
||||
return (long)ret;
|
||||
}
|
||||
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters,bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_ARCH_X86_64_SYSCALL_ARCH_H */
|
||||
+26
-7
@@ -1,6 +1,7 @@
|
||||
# vlibc — benchmark harnesses (musts/BENCHMARKING.md).
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
# Build-dir include FIRST (generated features.h lives there in VPATH builds).
|
||||
AM_CPPFLAGS = -I$(top_builddir)/include -I$(top_srcdir)/include
|
||||
AM_CFLAGS = @VLIBC_CFLAGS@
|
||||
|
||||
# Built only on demand (via `make bench`), so `make all` does not need the
|
||||
@@ -9,18 +10,30 @@ EXTRA_PROGRAMS = bench_vlibc bench_string
|
||||
bench_vlibc_SOURCES = bench_vlibc.c
|
||||
bench_string_SOURCES = bench_string.c
|
||||
|
||||
# bench_string is a system-headers-only translation unit (it times whichever
|
||||
# libc it is linked against), so it must NOT inherit AM_CPPFLAGS: -I include
|
||||
# would pull vlibc's self-contained <stddef.h>/<string.h> into the same TU as
|
||||
# the system <stdio.h>/<time.h> and double-define size_t/NULL/offsetof.
|
||||
# bench_vlibc keeps the inherited -I include.
|
||||
# Both harness TUs are system-headers-only translation units (they time
|
||||
# whichever libc they are linked against), so they must NOT inherit
|
||||
# AM_CPPFLAGS: -I include would pull vlibc's self-contained
|
||||
# <stdarg.h>/<stddef.h>/<string.h> into the same TU as the system
|
||||
# <stdio.h>/<time.h> and double-define size_t/NULL/offsetof (or hide the
|
||||
# compiler's internal <stdarg.h> behind vlibc's). The vlibc function under
|
||||
# test is reached through the adapter TU bench_vlibc_under.c, which is
|
||||
# compiled with vlibc's headers and exposes the call to the harness through
|
||||
# its own declaration (see that file).
|
||||
bench_vlibc_CPPFLAGS =
|
||||
bench_string_CPPFLAGS =
|
||||
|
||||
# Adapter TU for bench_vlibc: vlibc's headers only, so it must NOT inherit
|
||||
# the (empty) per-target CPPFLAGS above; it gets the include path back
|
||||
# explicitly.
|
||||
bench_vlibc_under.o: bench_vlibc_under.c
|
||||
$(AM_V_CC)$(CC) -I$(top_srcdir)/include $(CPPFLAGS) $(AM_CFLAGS) \
|
||||
$(CFLAGS) -c -o $@ $<
|
||||
|
||||
# By default benchmarks link against vlibc itself. --with-libc=glibc links the
|
||||
# same harness against the system glibc; --with-libc=musl builds it with
|
||||
# musl-gcc for the musl reference.
|
||||
if BENCH_LINK_VLIBC
|
||||
bench_vlibc_LDADD = ../libvlibc.la
|
||||
bench_vlibc_LDADD = bench_vlibc_under.o ../libvlibc.la
|
||||
bench_string_LDADD = ../libvlibc.la
|
||||
else
|
||||
bench_vlibc_LDADD =
|
||||
@@ -37,6 +50,12 @@ endif
|
||||
# binary from the previous configuration and the wrong libc would be measured.
|
||||
EXTRA_bench_string_DEPENDENCIES = $(top_builddir)/config.status
|
||||
EXTRA_bench_vlibc_DEPENDENCIES = $(top_builddir)/config.status
|
||||
bench_vlibc_DEPENDENCIES = bench_vlibc_under.o
|
||||
|
||||
# The adapter TU is built by a hand-rolled rule (per-TU include paths), so it
|
||||
# is not picked up by automake's automatic distribution/cleaning.
|
||||
EXTRA_DIST = bench_vlibc_under.c
|
||||
CLEANFILES = bench_vlibc_under.o
|
||||
|
||||
# bench_vlibc.c calls vlibc_version(), which exists only in vlibc — under
|
||||
# --with-libc=glibc/musl it cannot link, so it is built and run only in the
|
||||
|
||||
@@ -149,5 +149,14 @@ main(void)
|
||||
printf("%llu\n", sink);
|
||||
}
|
||||
|
||||
/*
|
||||
* Explicit flush: this harness is a host program, but under
|
||||
* --with-libc=vlibc its DT_NEEDED order puts libvlibc.so before
|
||||
* libc.so.6, so the exit() that runs at process end is vlibc's — which
|
||||
* does not flush stdio yet (that hook lands with the stdio todo).
|
||||
* Without the flush the timed results above are lost.
|
||||
*/
|
||||
fflush(stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -5,16 +5,29 @@
|
||||
* This stub times vlibc_version() and is the skeleton that per-component
|
||||
* benchmarks build on. Reconfigure with --with-libc=musl or --with-libc=glibc
|
||||
* to link the same harness against a reference libc for comparison.
|
||||
*
|
||||
* This TU is deliberately a HOST-headers-only translation unit: it includes
|
||||
* no vlibc header, because vlibc's self-contained
|
||||
* <stdarg.h>/<stddef.h>/<limits.h>/<float.h> shadow GCC's internal headers
|
||||
* and mixing them with the system <stdio.h>/<time.h> hard-errors. The
|
||||
* function under test is reached through the adapter TU
|
||||
* bench_vlibc_under.c, which is compiled with vlibc's headers and exposes
|
||||
* the call through its own declaration here.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <vlibc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
/* Adapter entry (bench_vlibc_under.c); declared here rather than including
|
||||
* <vlibc.h>, which this host-header TU must not do. The adapter also drops
|
||||
* vlibc_version()'s __attribute__((const)), so the timed loop really
|
||||
* executes the call. */
|
||||
const char *
|
||||
bench_vlibc_version(void);
|
||||
|
||||
#define ITERATIONS 100000000ULL
|
||||
|
||||
int
|
||||
@@ -34,7 +47,7 @@ main(void)
|
||||
|
||||
for (unsigned long long i = 0; i < ITERATIONS; i++)
|
||||
{
|
||||
version = vlibc_version();
|
||||
version = bench_vlibc_version();
|
||||
}
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &end) != 0)
|
||||
@@ -49,5 +62,13 @@ main(void)
|
||||
printf("vlibc_version() x %llu: %.3f s (%.2f ns/call), version=%s\n", ITERATIONS, seconds,
|
||||
seconds * 1000000000.0 / ITERATIONS, (const char *)version);
|
||||
|
||||
/*
|
||||
* Explicit flush: this harness is a host program, but its DT_NEEDED order
|
||||
* puts libvlibc.so before libc.so.6, so the exit() that runs at process
|
||||
* end is vlibc's — which does not flush stdio yet (that hook lands with
|
||||
* the stdio todo). Without the flush the buffered result above is lost.
|
||||
*/
|
||||
fflush(stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* vlibc-under-test adapter for the bench_vlibc harness (todo 6 bench fix).
|
||||
*
|
||||
* This translation unit is compiled with vlibc's OWN headers (-I ../include)
|
||||
* and is the ONLY TU in the benchmark that may include a vlibc header. The
|
||||
* harness TU (bench_vlibc.c) must never include one: vlibc's self-contained
|
||||
* <stdarg.h>/<stddef.h>/<limits.h>/<float.h> shadow GCC's internal headers,
|
||||
* so any TU that mixes a vlibc header with the host <stdio.h>/<time.h>
|
||||
* fails to compile (glibc's <stdio.h> needs __gnuc_va_list, which only the
|
||||
* compiler's internal <stdarg.h> defines). The adapter isolates the
|
||||
* vlibc-facing call here and exposes it to the host-header harness through
|
||||
* its own declaration.
|
||||
*
|
||||
* Passing through the adapter also drops vlibc_version()'s
|
||||
* __attribute__((const)) at the harness call site: the harness sees a plain
|
||||
* external function, so the timed loop genuinely executes the call instead
|
||||
* of being hoisted out by the optimizer.
|
||||
*/
|
||||
|
||||
#include <vlibc.h>
|
||||
|
||||
const char *
|
||||
bench_vlibc_version(void);
|
||||
|
||||
const char *
|
||||
bench_vlibc_version(void)
|
||||
{
|
||||
return vlibc_version();
|
||||
}
|
||||
@@ -686,6 +686,11 @@ PROFILE_GE_2_TRUE
|
||||
vlibc_level
|
||||
vlibc_profile
|
||||
VLIBC_CFLAGS
|
||||
am__fastdepCCAS_FALSE
|
||||
am__fastdepCCAS_TRUE
|
||||
CCASDEPMODE
|
||||
CCASFLAGS
|
||||
CCAS
|
||||
am__fastdepCC_FALSE
|
||||
am__fastdepCC_TRUE
|
||||
CCDEPMODE
|
||||
@@ -806,6 +811,8 @@ CFLAGS
|
||||
LDFLAGS
|
||||
LIBS
|
||||
CPPFLAGS
|
||||
CCAS
|
||||
CCASFLAGS
|
||||
LT_SYS_LIBRARY_PATH'
|
||||
|
||||
|
||||
@@ -1482,6 +1489,8 @@ Some influential environment variables:
|
||||
LIBS libraries to pass to the linker, e.g. -l<library>
|
||||
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
|
||||
you have headers in a nonstandard directory <include dir>
|
||||
CCAS assembler compiler command (defaults to CC)
|
||||
CCASFLAGS assembler compiler flags (defaults to CFLAGS)
|
||||
LT_SYS_LIBRARY_PATH
|
||||
User-defined run-time library search path.
|
||||
|
||||
@@ -4931,6 +4940,143 @@ else
|
||||
fi
|
||||
|
||||
|
||||
# AM_PROG_AS: library sources include raw x86_64 assembly (the setjmp family
|
||||
# in src/setjmp/x86_64/*.s), which automake compiles through CCAS/CCASFLAGS.
|
||||
# By default we simply use the C compiler to build assembly code.
|
||||
|
||||
test "${CCAS+set}" = set || CCAS=$CC
|
||||
test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS
|
||||
|
||||
|
||||
|
||||
depcc="$CCAS" am_compiler_list=
|
||||
|
||||
{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
|
||||
printf %s "checking dependency style of $depcc... " >&6; }
|
||||
if test ${am_cv_CCAS_dependencies_compiler_type+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else case e in #(
|
||||
e) if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
|
||||
# We make a subdir and do the tests there. Otherwise we can end up
|
||||
# making bogus files that we don't know about and never remove. For
|
||||
# instance it was reported that on HP-UX the gcc test will end up
|
||||
# making a dummy file named 'D' -- because '-MD' means "put the output
|
||||
# in D".
|
||||
rm -rf conftest.dir
|
||||
mkdir conftest.dir
|
||||
# Copy depcomp to subdir because otherwise we won't find it if we're
|
||||
# using a relative directory.
|
||||
cp "$am_depcomp" conftest.dir
|
||||
cd conftest.dir
|
||||
# We will build objects and dependencies in a subdirectory because
|
||||
# it helps to detect inapplicable dependency modes. For instance
|
||||
# both Tru64's cc and ICC support -MD to output dependencies as a
|
||||
# side effect of compilation, but ICC will put the dependencies in
|
||||
# the current directory while Tru64 will put them in the object
|
||||
# directory.
|
||||
mkdir sub
|
||||
|
||||
am_cv_CCAS_dependencies_compiler_type=none
|
||||
if test "$am_compiler_list" = ""; then
|
||||
am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
|
||||
fi
|
||||
am__universal=false
|
||||
|
||||
|
||||
for depmode in $am_compiler_list; do
|
||||
# Setup a source with many dependencies, because some compilers
|
||||
# like to wrap large dependency lists on column 80 (with \), and
|
||||
# we should not choose a depcomp mode which is confused by this.
|
||||
#
|
||||
# We need to recreate these files for each test, as the compiler may
|
||||
# overwrite some of them when testing with obscure command lines.
|
||||
# This happens at least with the AIX C compiler.
|
||||
: > sub/conftest.c
|
||||
for i in 1 2 3 4 5 6; do
|
||||
echo '#include "conftst'$i'.h"' >> sub/conftest.c
|
||||
# Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
|
||||
# Solaris 10 /bin/sh.
|
||||
echo '/* dummy */' > sub/conftst$i.h
|
||||
done
|
||||
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
|
||||
|
||||
# We check with '-c' and '-o' for the sake of the "dashmstdout"
|
||||
# mode. It turns out that the SunPro C++ compiler does not properly
|
||||
# handle '-M -o', and we need to detect this. Also, some Intel
|
||||
# versions had trouble with output in subdirs.
|
||||
am__obj=sub/conftest.${OBJEXT-o}
|
||||
am__minus_obj="-o $am__obj"
|
||||
case $depmode in
|
||||
gcc)
|
||||
# This depmode causes a compiler race in universal mode.
|
||||
test "$am__universal" = false || continue
|
||||
;;
|
||||
nosideeffect)
|
||||
# After this tag, mechanisms are not by side-effect, so they'll
|
||||
# only be used when explicitly requested.
|
||||
if test "x$enable_dependency_tracking" = xyes; then
|
||||
continue
|
||||
else
|
||||
break
|
||||
fi
|
||||
;;
|
||||
msvc7 | msvc7msys | msvisualcpp | msvcmsys)
|
||||
# This compiler won't grok '-c -o', but also, the minuso test has
|
||||
# not run yet. These depmodes are late enough in the game, and
|
||||
# so weak that their functioning should not be impacted.
|
||||
am__obj=conftest.${OBJEXT-o}
|
||||
am__minus_obj=
|
||||
;;
|
||||
none) break ;;
|
||||
esac
|
||||
if depmode=$depmode \
|
||||
source=sub/conftest.c object=$am__obj \
|
||||
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
|
||||
$SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
|
||||
>/dev/null 2>conftest.err &&
|
||||
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
|
||||
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
|
||||
grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
|
||||
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
|
||||
# icc doesn't choke on unknown options, it will just issue warnings
|
||||
# or remarks (even with -Werror). So we grep stderr for any message
|
||||
# that says an option was ignored or not supported.
|
||||
# When given -MP, icc 7.0 and 7.1 complain thus:
|
||||
# icc: Command line warning: ignoring option '-M'; no argument required
|
||||
# The diagnosis changed in icc 8.0:
|
||||
# icc: Command line remark: option '-MP' not supported
|
||||
if (grep 'ignoring option' conftest.err ||
|
||||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
|
||||
am_cv_CCAS_dependencies_compiler_type=$depmode
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
cd ..
|
||||
rm -rf conftest.dir
|
||||
else
|
||||
am_cv_CCAS_dependencies_compiler_type=none
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: result: $am_cv_CCAS_dependencies_compiler_type" >&5
|
||||
printf '%s\n' "$am_cv_CCAS_dependencies_compiler_type" >&6; }
|
||||
CCASDEPMODE=depmode=$am_cv_CCAS_dependencies_compiler_type
|
||||
|
||||
if
|
||||
test "x$enable_dependency_tracking" != xno \
|
||||
&& test "$am_cv_CCAS_dependencies_compiler_type" = gcc3; then
|
||||
am__fastdepCCAS_TRUE=
|
||||
am__fastdepCCAS_FALSE='#'
|
||||
else
|
||||
am__fastdepCCAS_TRUE='#'
|
||||
am__fastdepCCAS_FALSE=
|
||||
fi
|
||||
|
||||
|
||||
{ printf '%s\n' "$as_me:${as_lineno-$LINENO}: checking whether the compiler is GCC" >&5
|
||||
printf %s "checking whether the compiler is GCC... " >&6; }
|
||||
|
||||
@@ -5186,7 +5332,16 @@ fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||
CFLAGS=$vlibc_save_CFLAGS
|
||||
|
||||
VLIBC_CFLAGS="-std=$vlibc_cstd -Wall -Wextra"
|
||||
# -fno-stack-protector: distro GCC builds enable -fstack-protector-strong by
|
||||
# default, which would make library objects reference __stack_chk_fail — a
|
||||
# symbol vlibc does not provide yet. Every make-check test binary and every
|
||||
# -nostdlib -static consumer links against the plain archive, and one
|
||||
# undefined __stack_chk_fail in it breaks the whole self-hosting link. A
|
||||
# bootstrap libc must build its own objects without the protector (the stack
|
||||
# protector runtime lands with the hardening work, after which this flag is
|
||||
# revisited). Tests compile with the same flag (tests/Makefile.am wiring in
|
||||
# Makefile.am).
|
||||
VLIBC_CFLAGS="-std=$vlibc_cstd -Wall -Wextra -fno-stack-protector"
|
||||
|
||||
|
||||
# ---- Compatibility profile ----------------------------------------------
|
||||
@@ -14607,6 +14762,10 @@ if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
|
||||
as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then
|
||||
as_fn_error $? "conditional \"am__fastdepCCAS\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${PROFILE_GE_2_TRUE}" && test -z "${PROFILE_GE_2_FALSE}"; then
|
||||
as_fn_error $? "conditional \"PROFILE_GE_2\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
|
||||
+13
-1
@@ -26,6 +26,9 @@ AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
# (including clang), so it cannot be used to enforce the single-compiler rule.
|
||||
# Verify the compiler is genuinely GCC, not a compatible one.
|
||||
AC_PROG_CC
|
||||
# AM_PROG_AS: library sources include raw x86_64 assembly (the setjmp family
|
||||
# in src/setjmp/x86_64/*.s), which automake compiles through CCAS/CCASFLAGS.
|
||||
AM_PROG_AS
|
||||
AC_MSG_CHECKING([whether the compiler is GCC])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
|
||||
#if !defined(__GNUC__) || defined(__clang__)
|
||||
@@ -58,7 +61,16 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],
|
||||
AC_MSG_ERROR([$CC does not support -std=$vlibc_cstd])])
|
||||
CFLAGS=$vlibc_save_CFLAGS
|
||||
|
||||
AC_SUBST([VLIBC_CFLAGS], ["-std=$vlibc_cstd -Wall -Wextra"])
|
||||
# -fno-stack-protector: distro GCC builds enable -fstack-protector-strong by
|
||||
# default, which would make library objects reference __stack_chk_fail — a
|
||||
# symbol vlibc does not provide yet. Every make-check test binary and every
|
||||
# -nostdlib -static consumer links against the plain archive, and one
|
||||
# undefined __stack_chk_fail in it breaks the whole self-hosting link. A
|
||||
# bootstrap libc must build its own objects without the protector (the stack
|
||||
# protector runtime lands with the hardening work, after which this flag is
|
||||
# revisited). Tests compile with the same flag (tests/Makefile.am wiring in
|
||||
# Makefile.am).
|
||||
AC_SUBST([VLIBC_CFLAGS], ["-std=$vlibc_cstd -Wall -Wextra -fno-stack-protector"])
|
||||
|
||||
# ---- Compatibility profile ----------------------------------------------
|
||||
# Exactly one of the five profiles is active. They are mutually exclusive;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* vlibc — x86_64 _start (todo 3).
|
||||
*
|
||||
* The kernel enters the program here with the initial stack holding, in
|
||||
* order from %rsp: argc, argv[0..argc-1], a NULL argv terminator, the
|
||||
* environment strings, a NULL envp terminator, and the auxiliary vector.
|
||||
* The kernel leaves %rsp 16-byte aligned at entry.
|
||||
*
|
||||
* _start forwards (main, argc, argv, envp) to __libc_start_main, matching
|
||||
* vlibc's startup signature (src/start/start.h). The frame pointer is
|
||||
* zeroed first so the outermost frame terminates backtraces, and %rsp is
|
||||
* 16-byte aligned at the call site per the SysV AMD64 ABI. __libc_start_main
|
||||
* never returns (it ends in exit), so the hlt loop below is unreachable; it
|
||||
* exists only to satisfy the assembler and to fault loudly (hlt in user
|
||||
* mode raises #GP) if a broken build ever falls through.
|
||||
*/
|
||||
|
||||
.text
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
xor %ebp, %ebp # outermost frame: zero frame pointer
|
||||
mov %rsp, %r8 # keep the raw initial stack for envp
|
||||
pop %rsi # rsi = argc
|
||||
mov %rsp, %rdx # rdx = argv
|
||||
lea 16(%r8, %rsi, 8), %rcx # rcx = envp = initial rsp + 8 + 8*(argc+1)
|
||||
lea main(%rip), %rdi # rdi = &main
|
||||
and $-16, %rsp # ABI: rsp % 16 == 0 at the call site
|
||||
call __libc_start_main # _Noreturn: routes main's result into exit
|
||||
1: hlt
|
||||
jmp 1b
|
||||
.size _start, . - _start
|
||||
|
||||
.section .note.GNU-stack, "", @progbits
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef VLIBC_ASSERT_H
|
||||
#define VLIBC_ASSERT_H
|
||||
|
||||
/*
|
||||
* vlibc — <assert.h>.
|
||||
*
|
||||
* The assert diagnostic macro. When NDEBUG is defined the macro expands to
|
||||
* ((void)0) and the expression is not evaluated; otherwise a false result
|
||||
* calls the library-internal __vlibc_assert_fail(), which writes the
|
||||
* diagnostic to fd 2 through the raw syscall layer and terminates the
|
||||
* process with __builtin_trap(). The helper deliberately does not depend on
|
||||
* abort(), exit() or stdio: those land in later todos, and an assertion
|
||||
* failure must work even when the runtime is half-initialized.
|
||||
*
|
||||
* static_assert is a C23 keyword; for C17 and older this header maps it onto
|
||||
* _Static_assert, and _Static_assert itself remains usable in every mode.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
/* NDEBUG: strip the check entirely, matching the standard. */
|
||||
#ifdef NDEBUG
|
||||
#define assert(expr) ((void)0)
|
||||
#else
|
||||
/*
|
||||
* Terminate after writing the failing assertion to fd 2; never returns.
|
||||
* Implemented in src/internal/assert_fail.c. The name sits in the
|
||||
* implementation-reserved namespace because it is libc plumbing, not
|
||||
* public API.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
__vlibc_assert_fail(const char *expr, const char *file,
|
||||
int line, // NOLINT(bugprone-reserved-identifier)
|
||||
const char *func);
|
||||
|
||||
#define assert(expr) ((expr) ? (void)0 : __vlibc_assert_fail(#expr, __FILE__, __LINE__, __func__))
|
||||
#endif
|
||||
|
||||
#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L)
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_ASSERT_H */
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef VLIBC_CPIO_H
|
||||
#define VLIBC_CPIO_H
|
||||
|
||||
/*
|
||||
* vlibc — <cpio.h>.
|
||||
*
|
||||
* Constants for cpio archive formats (POSIX). Constants only: the file and
|
||||
* mode bits used in "newc"/odc archive headers, and the octal magic string
|
||||
* of the odc format. The newc magic ("070701") is not standard and is
|
||||
* deliberately omitted.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_HAS_HEADER_CPIO_H
|
||||
|
||||
/* Octal magic of the odc ("old binary") archive format. */
|
||||
#define MAGIC "070707"
|
||||
|
||||
/* Mode bits (permissions) of an archived entry. */
|
||||
#define C_IRUSR 0000400
|
||||
#define C_IWUSR 0000200
|
||||
#define C_IXUSR 0000100
|
||||
#define C_IRGRP 0000040
|
||||
#define C_IWGRP 0000020
|
||||
#define C_IXGRP 0000010
|
||||
#define C_IROTH 0000004
|
||||
#define C_IWOTH 0000002
|
||||
#define C_IXOTH 0000001
|
||||
|
||||
/* Set-id and sticky bits of an archived entry. */
|
||||
#define C_ISUID 0004000
|
||||
#define C_ISGID 0002000
|
||||
#define C_ISVTX 0001000
|
||||
|
||||
/* File type bits of an archived entry. */
|
||||
#define C_ISDIR 0040000
|
||||
#define C_ISFIFO 0010000
|
||||
#define C_ISREG 0100000
|
||||
#define C_ISBLK 0060000
|
||||
#define C_ISCHR 0020000
|
||||
#define C_ISCTG 0110000
|
||||
#define C_ISLNK 0120000
|
||||
#define C_ISSOCK 0140000
|
||||
|
||||
#endif /* VLIBC_HAS_HEADER_CPIO_H */
|
||||
|
||||
#endif /* VLIBC_CPIO_H */
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
#ifndef VLIBC_CTYPE_H
|
||||
#define VLIBC_CTYPE_H
|
||||
|
||||
/*
|
||||
* vlibc — <ctype.h>.
|
||||
*
|
||||
* Character classification and case mapping. Every function takes an int
|
||||
* that represents an unsigned char value or EOF (-1). The implementation
|
||||
* always casts the argument to unsigned char before any table lookup, so a
|
||||
* negative char value can never index out of bounds; EOF classifies false
|
||||
* in every is* function, and tolower/toupper pass it (and every non-letter)
|
||||
* through unchanged. All results are for the "C" locale.
|
||||
*
|
||||
* Levels are cumulative (see include/vlibc/features.h):
|
||||
* Level 1 (onlyposix): ISO C — the classification and case-mapping
|
||||
* functions.
|
||||
* Level 2 (muslmimic): XSI — isascii, toascii.
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gates below always
|
||||
* see the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first. The classification functions return exactly 1 when the
|
||||
* character is in the class and 0 otherwise, never any other nonzero value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Level 1: ISO C (always present). */
|
||||
|
||||
/*
|
||||
* Nonzero when c is alphanumeric: a letter or a decimal digit.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isalnum(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is a letter.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isalpha(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is blank: space or horizontal tab.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isblank(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is a control character (0x00..0x1f or 0x7f).
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
iscntrl(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is a decimal digit.
|
||||
* const: bit arithmetic on the argument alone (also matches GCC's const
|
||||
* builtin declaration for isdigit).
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
isdigit(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c has a visible representation (printable, except space).
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isgraph(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is a lowercase letter.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
islower(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is printable (0x20..0x7e).
|
||||
* pure: the implementation is bit arithmetic on the argument alone, but
|
||||
* GCC's isprint builtin is declared pure, and a const redeclaration would
|
||||
* conflict with it.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isprint(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is a punctuation character: printable and not alphanumeric
|
||||
* and not space.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
ispunct(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is white space (space, form feed, newline, carriage
|
||||
* return, horizontal or vertical tab).
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isspace(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is an uppercase letter.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
isupper(int c);
|
||||
|
||||
/*
|
||||
* Nonzero when c is a hexadecimal digit (0-9, a-f, A-F).
|
||||
* const: bit arithmetic on the argument alone (also matches GCC's const
|
||||
* builtin declaration for isxdigit).
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
isxdigit(int c);
|
||||
|
||||
/*
|
||||
* Return the lowercase counterpart of c when c is an uppercase letter
|
||||
* ('A'..'Z' in the "C" locale); otherwise return c unchanged, including
|
||||
* EOF.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
tolower(int c);
|
||||
|
||||
/*
|
||||
* Return the uppercase counterpart of c when c is a lowercase letter
|
||||
* ('a'..'z' in the "C" locale); otherwise return c unchanged, including
|
||||
* EOF.
|
||||
* pure: reads the classification table, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
toupper(int c);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* Nonzero when c is a 7-bit ASCII value (0..127). EOF and negative values
|
||||
* are outside the range.
|
||||
* const: depends only on its argument.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
isascii(int c);
|
||||
|
||||
/*
|
||||
* Clear every bit above the low 7 of c (c & 0x7f).
|
||||
* const: depends only on its argument.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
toascii(int c);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_CTYPE_H */
|
||||
@@ -0,0 +1,179 @@
|
||||
#ifndef VLIBC_DIRENT_H
|
||||
#define VLIBC_DIRENT_H
|
||||
|
||||
/*
|
||||
* vlibc — <dirent.h>.
|
||||
*
|
||||
* Directory streams: opendir/fdopendir/readdir/rewinddir/closedir,
|
||||
* seekdir/telldir/dirfd (POSIX.1-2008 base) iterate a directory via the
|
||||
* SYS_getdents64 kernel ABI, exposing one entry at a time.
|
||||
*
|
||||
* Level 1 (onlyposix): the base family above.
|
||||
* Level 2 (muslmimic): scandir, alphasort (XSI) and readdir_r
|
||||
* (obsolescent). versionsort is GNU and is not
|
||||
* provided.
|
||||
*
|
||||
* struct dirent mirrors the x86_64 getdents64 record layout (verified by
|
||||
* the static assertions below): d_ino/d_off are 64-bit, d_reclen is the
|
||||
* kernel record length, d_type carries the DT_* file type, and d_name is a
|
||||
* 256-byte NUL-terminated name buffer (NAME_MAX 255 + NUL).
|
||||
*
|
||||
* DIR is an opaque handle whose layout lives in the internal header
|
||||
* src/dirent/dirent_impl.h. None of these declarations carries an intent
|
||||
* attribute: every function performs I/O with side effects and reports
|
||||
* failures through errno, so const/pure would be unsound (the same
|
||||
* rationale sys/stat.h documents for its I/O family).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* File type values for the d_type member of struct dirent (kernel UAPI). */
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_FIFO 1
|
||||
#define DT_CHR 2
|
||||
#define DT_DIR 4
|
||||
#define DT_BLK 6
|
||||
#define DT_REG 8
|
||||
#define DT_LNK 10
|
||||
#define DT_SOCK 12
|
||||
#define DT_WHT 14
|
||||
|
||||
/*
|
||||
* One directory entry. Layout equals the x86_64 struct linux_dirent64
|
||||
* fields 0..18 verbatim; the name then follows at offset 19, stored in the
|
||||
* conventional 256-byte buffer.
|
||||
*/
|
||||
struct dirent
|
||||
{
|
||||
ino_t d_ino; /* 0: inode number */
|
||||
off_t d_off; /* 8: offset of the next entry (seek cookie) */
|
||||
unsigned short d_reclen; /* 16: length of the kernel record */
|
||||
unsigned char d_type; /* 18: DT_* file type */
|
||||
char d_name[256]; /* 19: NUL-terminated file name */
|
||||
};
|
||||
|
||||
/* Pin the layout to the x86_64 kernel ABI. */
|
||||
_Static_assert(sizeof(struct dirent) == 280, "struct dirent must match the x86_64 getdents64 layout");
|
||||
_Static_assert(offsetof(struct dirent, d_off) == 8, "d_off must sit at offset 8");
|
||||
_Static_assert(offsetof(struct dirent, d_type) == 18, "d_type must sit at offset 18");
|
||||
_Static_assert(offsetof(struct dirent, d_name) == 19, "d_name must sit at offset 19");
|
||||
|
||||
/*
|
||||
* Opaque directory stream handle. The struct tag stays usable from the
|
||||
* internal layout header, which defines struct vlibc_DIR (see
|
||||
* src/dirent/dirent_impl.h).
|
||||
*/
|
||||
typedef struct vlibc_DIR DIR;
|
||||
|
||||
/*
|
||||
* Open the directory named by path for reading and return a directory
|
||||
* stream positioned at its first entry, or NULL with errno set (a
|
||||
* non-directory path fails with ENOTDIR). The stream owns a descriptor
|
||||
* that closedir() releases.
|
||||
*/
|
||||
DIR *
|
||||
opendir(const char *path);
|
||||
|
||||
/*
|
||||
* Like opendir(), but over the already-open descriptor fd, which must
|
||||
* refer to a directory (validated with fstat). On failure NULL is returned
|
||||
* with errno set and fd is left open and owned by the caller.
|
||||
*/
|
||||
DIR *
|
||||
fdopendir(int fd);
|
||||
|
||||
/*
|
||||
* Return the next directory entry of dir, or NULL at the end of the
|
||||
* directory (errno untouched) or on error (errno set). The result points
|
||||
* at storage owned by dir and is valid until the next call to readdir,
|
||||
* rewinddir, seekdir, or closedir on the same stream.
|
||||
*/
|
||||
struct dirent *
|
||||
readdir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Reset dir to the beginning of the directory: the next readdir returns
|
||||
* the first entry again. Never fails.
|
||||
*/
|
||||
void
|
||||
rewinddir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Close dir, releasing its descriptor and storage. Return 0, or -1 with
|
||||
* errno set if the underlying close fails.
|
||||
*/
|
||||
int
|
||||
closedir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Reposition dir so the next readdir resumes at the location loc, which
|
||||
* must be a value previously returned by telldir (a getdents64 seek
|
||||
* cookie). Never fails.
|
||||
*/
|
||||
void
|
||||
seekdir(DIR *dir, long loc);
|
||||
|
||||
/*
|
||||
* Return the current location of dir, for a later seekdir. The location is
|
||||
* the point after the entry most recently returned by readdir; it becomes
|
||||
* indeterminate after rewinddir or closedir.
|
||||
*/
|
||||
long
|
||||
telldir(DIR *dir);
|
||||
|
||||
/*
|
||||
* Return the descriptor underlying dir. The descriptor stays owned by the
|
||||
* stream and remains valid until closedir.
|
||||
*/
|
||||
int
|
||||
dirfd(DIR *dir);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): XSI and obsolescent. */
|
||||
|
||||
/*
|
||||
* Reentrant readdir: store the next entry in *buf and set *result to buf;
|
||||
* at the end of the directory *result is NULL. Returns 0 at end of
|
||||
* directory, an error number on failure (errno is not used for the error
|
||||
* report), and leaves errno unmodified on success. Obsolescent.
|
||||
*/
|
||||
int
|
||||
readdir_r(DIR *restrict dir, struct dirent *restrict buf,
|
||||
struct dirent **restrict result);
|
||||
|
||||
/*
|
||||
* Read the whole directory named by path and store a malloc'd array of
|
||||
* malloc'd struct dirent copies in *res (both released with free; the
|
||||
* array is NULL-terminated with one extra NULL pointer). Only entries for
|
||||
* which sel is NULL or returns nonzero are kept; cmp, when non-NULL, sorts
|
||||
* the array (alphasort is the strcmp-on-name comparator). Returns the
|
||||
* number of entries, or -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
scandir(const char *path, struct dirent ***res,
|
||||
int (*sel)(const struct dirent *),
|
||||
int (*cmp)(const struct dirent **, const struct dirent **));
|
||||
|
||||
/*
|
||||
* Lexicographic comparator over the d_name fields of two struct dirent
|
||||
* pointers, for use as scandir's cmp argument. XSI.
|
||||
*/
|
||||
int
|
||||
alphasort(const struct dirent **a, const struct dirent **b);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_DIRENT_H */
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
#ifndef VLIBC_ERRNO_H
|
||||
#define VLIBC_ERRNO_H
|
||||
|
||||
/*
|
||||
* vlibc — <errno.h>.
|
||||
*
|
||||
* errno is per-thread state. It lives in a slot of the thread control block
|
||||
* (TCB), addressed relative to the x86_64 FS thread pointer; there is no
|
||||
* process-global errno object and no compiler-managed TLS (`__thread`) here.
|
||||
* The errno macro below expands to a dereference of __errno_location(), the
|
||||
* implementation-reserved accessor that returns the address of the calling
|
||||
* thread's errno slot. The offset of that slot inside the TCB is the ABI
|
||||
* constant VLIBC_TCB_ERRNO_OFF, owned by the internal TCB/ABI headers (see
|
||||
* src/internal/errno.h) — this public header is the single canonical home
|
||||
* for the errno macro, the __errno_location() declaration, and the E*
|
||||
* constant table, so internal code includes it instead of keeping a copy.
|
||||
*
|
||||
* Levels are cumulative (see include/vlibc/features.h):
|
||||
* Level 1 (onlyposix): POSIX base — strerror.
|
||||
* Level 2 (muslmimic): XSI — strerror_r (the int-returning flavor; the
|
||||
* GNU char*-returning variant is level 3+ and is
|
||||
* deliberately not provided).
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gates below always
|
||||
* see the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first, and <stddef.h> for size_t.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return the address of the calling thread's errno slot.
|
||||
*
|
||||
* Requires the thread pointer (FS) to be initialized to a TCB whose first
|
||||
* word is the TCB's own address — the startup todo owns that bootstrap and
|
||||
* sets it before any code that can touch errno runs. This function itself
|
||||
* performs no setup and no other TCB access.
|
||||
*
|
||||
* The name sits in the implementation-reserved namespace deliberately: it is
|
||||
* this libc's private errno accessor, not public API.
|
||||
*/
|
||||
int *
|
||||
__errno_location(void); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
/* The conventional errno lvalue; resolves to the caller's TCB slot. */
|
||||
#define errno (*__errno_location())
|
||||
|
||||
/*
|
||||
* Error numbers: the Linux errno ABI (asm-generic/errno-base.h plus
|
||||
* asm-generic/errno.h, which is the table x86_64 uses), transcribed as
|
||||
* kernel-ABI facts. These values are fixed and shared with the kernel.
|
||||
* Values 41 and 58 do not exist in the ABI (EWOULDBLOCK and EDEADLOCK are
|
||||
* aliases of EAGAIN and EDEADLK); EHWPOISON is the last defined value.
|
||||
*/
|
||||
|
||||
#define EPERM 1 /* Operation not permitted */
|
||||
#define ENOENT 2 /* No such file or directory */
|
||||
#define ESRCH 3 /* No such process */
|
||||
#define EINTR 4 /* Interrupted system call */
|
||||
#define EIO 5 /* I/O error */
|
||||
#define ENXIO 6 /* No such device or address */
|
||||
#define E2BIG 7 /* Argument list too long */
|
||||
#define ENOEXEC 8 /* Exec format error */
|
||||
#define EBADF 9 /* Bad file descriptor */
|
||||
#define ECHILD 10 /* No child processes */
|
||||
#define EAGAIN 11 /* Try again */
|
||||
#define ENOMEM 12 /* Out of memory */
|
||||
#define EACCES 13 /* Permission denied */
|
||||
#define EFAULT 14 /* Bad address */
|
||||
#define ENOTBLK 15 /* Block device required */
|
||||
#define EBUSY 16 /* Device or resource busy */
|
||||
#define EEXIST 17 /* File exists */
|
||||
#define EXDEV 18 /* Cross-device link */
|
||||
#define ENODEV 19 /* No such device */
|
||||
#define ENOTDIR 20 /* Not a directory */
|
||||
#define EISDIR 21 /* Is a directory */
|
||||
#define EINVAL 22 /* Invalid argument */
|
||||
#define ENFILE 23 /* File table overflow */
|
||||
#define EMFILE 24 /* Too many open files */
|
||||
#define ENOTTY 25 /* Not a typewriter */
|
||||
#define ETXTBSY 26 /* Text file busy */
|
||||
#define EFBIG 27 /* File too large */
|
||||
#define ENOSPC 28 /* No space left on device */
|
||||
#define ESPIPE 29 /* Illegal seek */
|
||||
#define EROFS 30 /* Read-only file system */
|
||||
#define EMLINK 31 /* Too many links */
|
||||
#define EPIPE 32 /* Broken pipe */
|
||||
#define EDOM 33 /* Math argument out of domain of func */
|
||||
#define ERANGE 34 /* Math result not representable */
|
||||
#define EDEADLK 35 /* Resource deadlock would occur */
|
||||
#define ENAMETOOLONG 36 /* File name too long */
|
||||
#define ENOLCK 37 /* No record locks available */
|
||||
#define ENOSYS 38 /* Invalid system call number */
|
||||
#define ENOTEMPTY 39 /* Directory not empty */
|
||||
#define ELOOP 40 /* Too many symbolic links encountered */
|
||||
#define EWOULDBLOCK EAGAIN /* Operation would block */
|
||||
#define ENOMSG 42 /* No message of desired type */
|
||||
#define EIDRM 43 /* Identifier removed */
|
||||
#define ECHRNG 44 /* Channel number out of range */
|
||||
#define EL2NSYNC 45 /* Level 2 not synchronized */
|
||||
#define EL3HLT 46 /* Level 3 halted */
|
||||
#define EL3RST 47 /* Level 3 reset */
|
||||
#define ELNRNG 48 /* Link number out of range */
|
||||
#define EUNATCH 49 /* Protocol driver not attached */
|
||||
#define ENOCSI 50 /* No CSI structure available */
|
||||
#define EL2HLT 51 /* Level 2 halted */
|
||||
#define EBADE 52 /* Invalid exchange */
|
||||
#define EBADR 53 /* Invalid request descriptor */
|
||||
#define EXFULL 54 /* Exchange full */
|
||||
#define ENOANO 55 /* No anode */
|
||||
#define EBADRQC 56 /* Invalid request code */
|
||||
#define EBADSLT 57 /* Invalid slot */
|
||||
#define EDEADLOCK EDEADLK /* File locking deadlock error */
|
||||
#define EBFONT 59 /* Bad font file format */
|
||||
#define ENOSTR 60 /* Device not a stream */
|
||||
#define ENODATA 61 /* No data available */
|
||||
#define ETIME 62 /* Timer expired */
|
||||
#define ENOSR 63 /* Out of streams resources */
|
||||
#define ENONET 64 /* Machine is not on the network */
|
||||
#define ENOPKG 65 /* Package not installed */
|
||||
#define EREMOTE 66 /* Object is remote */
|
||||
#define ENOLINK 67 /* Link has been severed */
|
||||
#define EADV 68 /* Advertise error */
|
||||
#define ESRMNT 69 /* Srmount error */
|
||||
#define ECOMM 70 /* Communication error on send */
|
||||
#define EPROTO 71 /* Protocol error */
|
||||
#define EMULTIHOP 72 /* Multihop attempted */
|
||||
#define EDOTDOT 73 /* RFS specific error */
|
||||
#define EBADMSG 74 /* Not a data message */
|
||||
#define EOVERFLOW 75 /* Value too large for defined data type */
|
||||
#define ENOTUNIQ 76 /* Name not unique on network */
|
||||
#define EBADFD 77 /* File descriptor in bad state */
|
||||
#define EREMCHG 78 /* Remote address changed */
|
||||
#define ELIBACC 79 /* Can not access a needed shared library */
|
||||
#define ELIBBAD 80 /* Accessing a corrupted shared library */
|
||||
#define ELIBSCN 81 /* .lib section in a.out corrupted */
|
||||
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
|
||||
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
|
||||
#define EILSEQ 84 /* Illegal byte sequence */
|
||||
#define ERESTART 85 /* Interrupted system call should be restarted */
|
||||
#define ESTRPIPE 86 /* Streams pipe error */
|
||||
#define EUSERS 87 /* Too many users */
|
||||
#define ENOTSOCK 88 /* Socket operation on non-socket */
|
||||
#define EDESTADDRREQ 89 /* Destination address required */
|
||||
#define EMSGSIZE 90 /* Message too long */
|
||||
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
|
||||
#define ENOPROTOOPT 92 /* Protocol not available */
|
||||
#define EPROTONOSUPPORT 93 /* Protocol not supported */
|
||||
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
|
||||
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
|
||||
#define EPFNOSUPPORT 96 /* Protocol family not supported */
|
||||
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
|
||||
#define EADDRINUSE 98 /* Address already in use */
|
||||
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
|
||||
#define ENETDOWN 100 /* Network is down */
|
||||
#define ENETUNREACH 101 /* Network is unreachable */
|
||||
#define ENETRESET 102 /* Network dropped connection because of reset */
|
||||
#define ECONNABORTED 103 /* Software caused connection abort */
|
||||
#define ECONNRESET 104 /* Connection reset by peer */
|
||||
#define ENOBUFS 105 /* No buffer space available */
|
||||
#define EISCONN 106 /* Transport endpoint is already connected */
|
||||
#define ENOTCONN 107 /* Transport endpoint is not connected */
|
||||
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
|
||||
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
|
||||
#define ETIMEDOUT 110 /* Connection timed out */
|
||||
#define ECONNREFUSED 111 /* Connection refused */
|
||||
#define EHOSTDOWN 112 /* Host is down */
|
||||
#define EHOSTUNREACH 113 /* No route to host */
|
||||
#define EALREADY 114 /* Operation already in progress */
|
||||
#define EINPROGRESS 115 /* Operation now in progress */
|
||||
#define ESTALE 116 /* Stale file handle */
|
||||
#define EUCLEAN 117 /* Structure needs cleaning */
|
||||
#define ENOTNAM 118 /* Not a XENIX named type file */
|
||||
#define ENAVAIL 119 /* No XENIX semaphores available */
|
||||
#define EISNAM 120 /* Is a named type file */
|
||||
#define EREMOTEIO 121 /* Remote I/O error */
|
||||
#define EDQUOT 122 /* Quota exceeded */
|
||||
#define ENOMEDIUM 123 /* No medium found */
|
||||
#define EMEDIUMTYPE 124 /* Wrong medium type */
|
||||
#define ECANCELED 125 /* Operation canceled */
|
||||
#define ENOKEY 126 /* Required key not available */
|
||||
#define EKEYEXPIRED 127 /* Key has expired */
|
||||
#define EKEYREVOKED 128 /* Key has been revoked */
|
||||
#define EKEYREJECTED 129 /* Key was rejected by service */
|
||||
#define EOWNERDEAD 130 /* Owner died */
|
||||
#define ENOTRECOVERABLE 131 /* State not recoverable */
|
||||
#define ERFKILL 132 /* Operation not possible due to RF-kill */
|
||||
#define EHWPOISON 133 /* Memory page has hardware error */
|
||||
|
||||
/*
|
||||
* Return a pointer to the message string for errnum. The string for a known
|
||||
* value points to immutable static storage and is safe to keep across calls;
|
||||
* an unknown value yields "Unknown error <errnum>", formatted into shared
|
||||
* static storage whose contents a later call may overwrite (unknown errno
|
||||
* values are a rare diagnostic path; see strerror.c). Never returns NULL.
|
||||
*
|
||||
* No intent attribute: the unknown-value path writes shared storage.
|
||||
*/
|
||||
char *
|
||||
strerror(int errnum);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* XSI strerror_r: copy the message for errnum into buf, truncated to buflen
|
||||
* and always NUL-terminated when buflen > 0, and return 0 — including for
|
||||
* unknown errnum values, whose text is "Unknown error <errnum>". When buflen
|
||||
* is 0 nothing is written and 0 is returned; when buf is NULL and buflen > 0,
|
||||
* EINVAL is returned. The GNU char*-returning variant is deliberately not
|
||||
* provided.
|
||||
*
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
int
|
||||
strerror_r(int errnum, char *buf, size_t buflen);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_ERRNO_H */
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#ifndef VLIBC_FCNTL_H
|
||||
#define VLIBC_FCNTL_H
|
||||
|
||||
/*
|
||||
* vlibc — <fcntl.h>.
|
||||
*
|
||||
* File control, record locks, and the open-flag constants (POSIX.1-2008 plus
|
||||
* the Linux x86_64 additions). Every O_*, F_*, AT_* and POSIX_FADV_* value
|
||||
* below is a Linux kernel UAPI fact for x86_64 (asm-generic/fcntl.h), transcribed in
|
||||
* hex with the kernel's octal form in a comment — not an invented number.
|
||||
* O_TMPFILE and O_SYNC are defined as flag combinations exactly as the
|
||||
* kernel defines them.
|
||||
*
|
||||
* Note on the *at constants: on x86_64 AT_EACCESS == AT_REMOVEDIR == 0x200;
|
||||
* the kernel disambiguates by syscall (faccessat vs unlinkat), so both names
|
||||
* carry the same value here.
|
||||
*
|
||||
* open/openat are deliberately NOT declared here: POSIX places them in this
|
||||
* header, but this project's single declaration site is <unistd.h> (todo 19;
|
||||
* see its header comment). creat() lives here — its canonical POSIX home.
|
||||
*
|
||||
* Level 2 (muslmimic/XSI): lockf plus F_LOCK/F_TLOCK/F_ULOCK/F_TEST. POSIX
|
||||
* puts lockf in <unistd.h>; that file is todo 19's and out of scope here,
|
||||
* and musl declares lockf in <fcntl.h> as well — the constants accompany the
|
||||
* declaration so the header stays self-contained. A later integration pass
|
||||
* may re-export the same declaration from <unistd.h> (identical, harmless).
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno (or,
|
||||
* for posix_fadvise/posix_fallocate, an error-number return), so const/pure
|
||||
* would be unsound — the same rationale unistd.h documents for its I/O
|
||||
* family.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- open(2) flag bits ---- */
|
||||
|
||||
/* Mask for the O_RDONLY/O_WRONLY/O_RDWR access mode. */
|
||||
#define O_ACCMODE 0x3
|
||||
|
||||
#define O_RDONLY 0x0 /* 00000000 */
|
||||
#define O_WRONLY 0x1 /* 00000001 */
|
||||
#define O_RDWR 0x2 /* 00000002 */
|
||||
|
||||
#define O_CREAT 0x40 /* 00000100 */
|
||||
#define O_EXCL 0x80 /* 00000200 */
|
||||
#define O_NOCTTY 0x100 /* 00000400 */
|
||||
#define O_TRUNC 0x200 /* 00001000 */
|
||||
#define O_APPEND 0x400 /* 00002000 */
|
||||
#define O_NONBLOCK 0x800 /* 00004000 */
|
||||
#define O_DSYNC 0x1000 /* 00010000 */
|
||||
#define O_ASYNC 0x2000 /* 00020000 */
|
||||
#define O_DIRECT 0x4000 /* 00040000 */
|
||||
#define O_LARGEFILE 0x8000 /* 00100000 */
|
||||
|
||||
#define O_DIRECTORY 0x10000 /* 00200000 */
|
||||
#define O_NOFOLLOW 0x20000 /* 00400000 */
|
||||
#define O_NOATIME 0x40000 /* 01000000 */
|
||||
#define O_CLOEXEC 0x80000 /* 02000000 */
|
||||
|
||||
/*
|
||||
* __O_SYNC/__O_TMPFILE are Linux-kernel names in the implementation-reserved
|
||||
* namespace (this libc IS "the implementation", and the kernel UAPI mandates
|
||||
* the exact spellings) — the NOLINT below is the house waiver for that.
|
||||
*/
|
||||
#define __O_SYNC 0x100000 /* NOLINT(bugprone-reserved-identifier) 04000000 */
|
||||
#define O_SYNC (__O_SYNC | O_DSYNC)
|
||||
#define O_PATH 0x200000 /* 010000000 */
|
||||
#define __O_TMPFILE 0x400000 /* NOLINT(bugprone-reserved-identifier) 020000000 */
|
||||
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
|
||||
|
||||
/* ---- fcntl(2) commands ---- */
|
||||
|
||||
#define F_DUPFD 0
|
||||
#define F_GETFD 1
|
||||
#define F_SETFD 2
|
||||
#define F_GETFL 3
|
||||
#define F_SETFL 4
|
||||
#define F_GETLK 5
|
||||
#define F_SETLK 6
|
||||
#define F_SETLKW 7
|
||||
#define F_SETOWN 8
|
||||
#define F_GETOWN 9
|
||||
#define F_SETSIG 10
|
||||
#define F_GETSIG 11
|
||||
|
||||
/* Linux-specific owner-identity commands (constants only; struct f_owner_ex
|
||||
* is out of POSIX scope and not provided). */
|
||||
#define F_SETOWN_EX 15
|
||||
#define F_GETOWN_EX 16
|
||||
#define F_GETOWNER_UIDS 17
|
||||
|
||||
/* Open-file-description (OFD) locks, Linux 3.15+. */
|
||||
#define F_OFD_GETLK 36
|
||||
#define F_OFD_SETLK 37
|
||||
#define F_OFD_SETLKW 38
|
||||
|
||||
#define F_DUPFD_CLOEXEC 1030 /* F_LINUX_SPECIFIC_BASE (1024) + 6 */
|
||||
|
||||
/* Close the descriptor on exec — the F_SETFD/F_GETFD flag. */
|
||||
#define FD_CLOEXEC 1
|
||||
|
||||
/* Record-lock types (struct flock l_type). */
|
||||
#define F_RDLCK 0
|
||||
#define F_WRLCK 1
|
||||
#define F_UNLCK 2
|
||||
|
||||
/* ---- *at(2) base-directory and behavior flags ---- */
|
||||
|
||||
#define AT_FDCWD (-100)
|
||||
#define AT_SYMLINK_NOFOLLOW 0x100
|
||||
#define AT_REMOVEDIR 0x200
|
||||
#define AT_SYMLINK_FOLLOW 0x400
|
||||
#define AT_EACCESS 0x200
|
||||
|
||||
/* ---- posix_fadvise(2) advice values ---- */
|
||||
|
||||
#define POSIX_FADV_NORMAL 0
|
||||
#define POSIX_FADV_RANDOM 1
|
||||
#define POSIX_FADV_SEQUENTIAL 2
|
||||
#define POSIX_FADV_WILLNEED 3
|
||||
#define POSIX_FADV_DONTNEED 4
|
||||
#define POSIX_FADV_NOREUSE 5
|
||||
|
||||
/*
|
||||
* Record-lock descriptor, ABI-identical to the x86_64 kernel layout
|
||||
* (asm-generic/fcntl.h: short, short, long, long, int — 32 bytes with
|
||||
* natural padding). On x86_64 the LFS and non-LFS layouts are one struct,
|
||||
* so there is no separate flock64 here.
|
||||
*/
|
||||
struct flock
|
||||
{
|
||||
short l_type; /* F_RDLCK, F_WRLCK, or F_UNLCK */
|
||||
short l_whence; /* SEEK_SET, SEEK_CUR, or SEEK_END (from <unistd.h>) */
|
||||
off_t l_start; /* relative offset of the locked region */
|
||||
off_t l_len; /* region length; 0 means through EOF */
|
||||
pid_t l_pid; /* PID of the process holding the lock (F_GETLK) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Perform one of the F_* control operations on fildes. Only the commands
|
||||
* that take a third argument read it from the varargs; all others pass 0,
|
||||
* which the kernel ignores. Return the command-specific result, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
fcntl(int fildes, int cmd, ...);
|
||||
|
||||
/*
|
||||
* Equivalent to open(path, O_WRONLY | O_CREAT | O_TRUNC, mode): create
|
||||
* path for writing, truncating any existing file, with mode masked by the
|
||||
* process umask. Return a file descriptor, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
creat(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Announce an expected access pattern for the range [offset, offset+len) of
|
||||
* fd (len 0 means through EOF). Unlike the rest of the family this returns
|
||||
* an error NUMBER directly — 0 on success, else the positive errno value
|
||||
* (e.g. EBADF, ESPIPE) — and errno is untouched (POSIX).
|
||||
*/
|
||||
int
|
||||
posix_fadvise(int fd, off_t offset, off_t len, int advice);
|
||||
|
||||
/*
|
||||
* Ensure storage is allocated for the range [offset, offset+len) of fd,
|
||||
* growing the file as needed. Returns an error number directly (0 on
|
||||
* success) and leaves errno untouched, like posix_fadvise (POSIX).
|
||||
*/
|
||||
int
|
||||
posix_fallocate(int fd, off_t offset, off_t len);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* lockf() commands (POSIX XSI; the kernel has no lockf syscall — these are
|
||||
* userspace cmd values). */
|
||||
#define F_ULOCK 0 /* unlock a previously locked region */
|
||||
#define F_LOCK 1 /* lock a region, blocking until available */
|
||||
#define F_TLOCK 2 /* try to lock; -1 with EACCES/EAGAIN if held */
|
||||
#define F_TEST 3 /* test a region for another process's lock */
|
||||
|
||||
/*
|
||||
* Apply or remove an exclusive POSIX record lock on [current offset,
|
||||
* current offset + len) of fd (len 0 means through EOF). XSI. Return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
lockf(int fd, int cmd, off_t len);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_FCNTL_H */
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef VLIBC_FLOAT_H
|
||||
#define VLIBC_FLOAT_H
|
||||
|
||||
/*
|
||||
* vlibc — <float.h>.
|
||||
*
|
||||
* Characteristics of floating-point types (C23). Every value is derived from
|
||||
* compiler predefined macros (__FLT_MANT_DIG__, __DBL_MAX__, ...), so this
|
||||
* header is fully self-contained. The values reflect the x86_64 SSE2
|
||||
* environment: FLT_RADIX 2, IEC 60559 semantics for float, double and long
|
||||
* double (the 80-bit extended type).
|
||||
*
|
||||
* FLT_ROUNDS is 1 (round to nearest): vlibc does not implement the floating
|
||||
* environment yet, so the default rounding mode is always active. The fenv
|
||||
* todo (#42) is expected to replace this with a live read of the x87/MXCSR
|
||||
* control word.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
/* Base of all floating-point types. */
|
||||
#define FLT_RADIX __FLT_RADIX__
|
||||
|
||||
/* Current rounding mode (see the header comment). */
|
||||
#define FLT_ROUNDS 1
|
||||
|
||||
/* Evaluation method (x86_64: all operations use the type's own range). */
|
||||
#define FLT_EVAL_METHOD __FLT_EVAL_METHOD__
|
||||
|
||||
/* IEC 60559 conformance of each type (C23). */
|
||||
#define FLT_IS_IEC_60559 __FLT_IS_IEC_60559__
|
||||
#define DBL_IS_IEC_60559 __DBL_IS_IEC_60559__
|
||||
#define LDBL_IS_IEC_60559 __LDBL_IS_IEC_60559__
|
||||
|
||||
/* float. */
|
||||
#define FLT_MANT_DIG __FLT_MANT_DIG__
|
||||
#define FLT_DECIMAL_DIG __FLT_DECIMAL_DIG__
|
||||
#define FLT_DIG __FLT_DIG__
|
||||
#define FLT_MIN_EXP __FLT_MIN_EXP__
|
||||
#define FLT_MIN_10_EXP __FLT_MIN_10_EXP__
|
||||
#define FLT_MAX_EXP __FLT_MAX_EXP__
|
||||
#define FLT_MAX_10_EXP __FLT_MAX_10_EXP__
|
||||
#define FLT_MAX __FLT_MAX__
|
||||
#define FLT_NORM_MAX __FLT_NORM_MAX__
|
||||
#define FLT_EPSILON __FLT_EPSILON__
|
||||
#define FLT_MIN __FLT_MIN__
|
||||
#define FLT_TRUE_MIN __FLT_DENORM_MIN__
|
||||
|
||||
/* double. */
|
||||
#define DBL_MANT_DIG __DBL_MANT_DIG__
|
||||
#define DBL_DECIMAL_DIG __DBL_DECIMAL_DIG__
|
||||
#define DBL_DIG __DBL_DIG__
|
||||
#define DBL_MIN_EXP __DBL_MIN_EXP__
|
||||
#define DBL_MIN_10_EXP __DBL_MIN_10_EXP__
|
||||
#define DBL_MAX_EXP __DBL_MAX_EXP__
|
||||
#define DBL_MAX_10_EXP __DBL_MAX_10_EXP__
|
||||
#define DBL_MAX __DBL_MAX__
|
||||
#define DBL_NORM_MAX __DBL_NORM_MAX__
|
||||
#define DBL_EPSILON __DBL_EPSILON__
|
||||
#define DBL_MIN __DBL_MIN__
|
||||
#define DBL_TRUE_MIN __DBL_DENORM_MIN__
|
||||
|
||||
/* long double (x86_64: 80-bit extended). */
|
||||
#define LDBL_MANT_DIG __LDBL_MANT_DIG__
|
||||
#define LDBL_DECIMAL_DIG __LDBL_DECIMAL_DIG__
|
||||
#define LDBL_DIG __LDBL_DIG__
|
||||
#define LDBL_MIN_EXP __LDBL_MIN_EXP__
|
||||
#define LDBL_MIN_10_EXP __LDBL_MIN_10_EXP__
|
||||
#define LDBL_MAX_EXP __LDBL_MAX_EXP__
|
||||
#define LDBL_MAX_10_EXP __LDBL_MAX_10_EXP__
|
||||
#define LDBL_MAX __LDBL_MAX__
|
||||
#define LDBL_NORM_MAX __LDBL_NORM_MAX__
|
||||
#define LDBL_EPSILON __LDBL_EPSILON__
|
||||
#define LDBL_MIN __LDBL_MIN__
|
||||
#define LDBL_TRUE_MIN __LDBL_DENORM_MIN__
|
||||
|
||||
#endif /* VLIBC_FLOAT_H */
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#ifndef VLIBC_GRP_H
|
||||
#define VLIBC_GRP_H
|
||||
|
||||
/*
|
||||
* vlibc — <grp.h>.
|
||||
*
|
||||
* The group database (todo 37). struct group mirrors /etc/group, whose
|
||||
* records are name:passwd:gid:member,member,... — the member list is a
|
||||
* comma-separated tail, empty when the group has no members. Everything
|
||||
* here is POSIX.1-2008 base (Level 1).
|
||||
*
|
||||
* The non-reentrant forms getgrnam/getgrgid/getgrent return a pointer to a
|
||||
* static structure whose contents — including the strings and the member
|
||||
* array it points at — are overwritten by the next call to any of the
|
||||
* three. getgrnam/getgrgid open and scan the whole file per call; getgrent
|
||||
* walks a cursor that setgrent rewinds and endgrent closes.
|
||||
*
|
||||
* The _r forms behave like their <pwd.h> counterparts: the entry is written
|
||||
* into caller storage (the gr_mem pointer array and the member name strings
|
||||
* both live inside the supplied buffer) and the return value is 0 on
|
||||
* success, ERANGE when the buffer is too small, or 0 with *result NULL when
|
||||
* no entry matches. errno is never touched: the error number IS the return
|
||||
* value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* One group database entry (/etc/group record). */
|
||||
struct group
|
||||
{
|
||||
char *gr_name; /* group name */
|
||||
char *gr_passwd; /* encrypted password; usually empty */
|
||||
gid_t gr_gid; /* numeric group id */
|
||||
char **gr_mem; /* NULL-terminated member-name array */
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the first entry whose name matches; NULL when absent. The result
|
||||
* points into shared static storage valid only until the next grp call.
|
||||
*/
|
||||
struct group *
|
||||
getgrnam(const char *name);
|
||||
|
||||
/*
|
||||
* Look up the first entry whose gid matches; NULL when absent. Result
|
||||
* storage is shared with getgrnam/getgrent as described above.
|
||||
*/
|
||||
struct group *
|
||||
getgrgid(gid_t gid);
|
||||
|
||||
/*
|
||||
* Return the next entry from the group stream, opening it on the first
|
||||
* call; NULL at end of file. The stream cursor advances per call.
|
||||
*/
|
||||
struct group *
|
||||
getgrent(void);
|
||||
|
||||
/* Rewind the group stream to its first entry. */
|
||||
void
|
||||
setgrent(void);
|
||||
|
||||
/* Close the group stream; a later getgrent reopens from the start. */
|
||||
void
|
||||
endgrent(void);
|
||||
|
||||
int
|
||||
getgrnam_r(const char *name, struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
int
|
||||
getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
int
|
||||
getgrent_r(struct group *gr, char *buf, size_t bufsize, struct group **result);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
#include <stdio.h> /* FILE */
|
||||
|
||||
/*
|
||||
* Return the next entry read from the given open stream, or NULL at end of
|
||||
* file or on a malformed/unopenable record; the entry is parsed as an
|
||||
* /etc/group record. Result storage is shared with getgrnam/getgrgid/
|
||||
* getgrent as described above, so the stream must not be closed until the
|
||||
* caller has copied the result. XSI.
|
||||
*/
|
||||
struct group *
|
||||
fgetgrent(FILE *stream);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_GRP_H */
|
||||
@@ -0,0 +1,314 @@
|
||||
#ifndef VLIBC_INTTYPES_H
|
||||
#define VLIBC_INTTYPES_H
|
||||
|
||||
/*
|
||||
* vlibc — <inttypes.h>.
|
||||
*
|
||||
* Extended integer type names, the imax* conversions of the widest signed
|
||||
* type, and the PRI and SCN formatted-I/O macros (C23 7.8). Every macro below
|
||||
* expands to a string literal holding the complete conversion specifier
|
||||
* (length modifier included) for the corresponding <stdint.h> type on the
|
||||
* x86_64 LP64 target, where the compiler predefined types are:
|
||||
*
|
||||
* int8_t/uint8_t signed char / unsigned char
|
||||
* int16_t/uint16_t short / unsigned short
|
||||
* int32_t/uint32_t int / unsigned int
|
||||
* int64_t/uint64_t long / unsigned long
|
||||
* int_leastN_t identical to the exact-width types
|
||||
* int_fast8_t signed char; int_fast16/32/64_t are long
|
||||
* intmax_t/uintmax_t long / unsigned long
|
||||
* intptr_t/uintptr_t long / unsigned long
|
||||
*
|
||||
* Design rule (per plan): the fprintf macros name the length modifier of
|
||||
* the PROMOTED argument — printf receives an int for every sub-int type,
|
||||
* so PRId8 is "d", not "hhd" — while the fscanf macros name the
|
||||
* exact-width modifier — scanf receives a pointer, so SCNd8 is "hhd".
|
||||
* Both forms convert the corresponding type correctly under C23 7.8.1;
|
||||
* glibc and musl instead use the exact-width modifier in both families,
|
||||
* which is equally conformant and indistinguishable in behavior for
|
||||
* correctly-typed arguments.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Quotient and remainder of the widest integer division (C23 7.8.1). */
|
||||
typedef struct imaxdiv_t
|
||||
{
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
/*
|
||||
* fprintf macros for the signed exact-width types (argument promotes to
|
||||
* int, or stays long for the 64-bit width).
|
||||
*/
|
||||
#define PRId8 "d"
|
||||
#define PRId16 "d"
|
||||
#define PRId32 "d"
|
||||
#define PRId64 "ld"
|
||||
|
||||
#define PRIi8 "i"
|
||||
#define PRIi16 "i"
|
||||
#define PRIi32 "i"
|
||||
#define PRIi64 "li"
|
||||
|
||||
/* fprintf macros for the unsigned exact-width types. */
|
||||
#define PRIo8 "o"
|
||||
#define PRIo16 "o"
|
||||
#define PRIo32 "o"
|
||||
#define PRIo64 "lo"
|
||||
|
||||
#define PRIu8 "u"
|
||||
#define PRIu16 "u"
|
||||
#define PRIu32 "u"
|
||||
#define PRIu64 "lu"
|
||||
|
||||
#define PRIx8 "x"
|
||||
#define PRIx16 "x"
|
||||
#define PRIx32 "x"
|
||||
#define PRIx64 "lx"
|
||||
|
||||
#define PRIX8 "X"
|
||||
#define PRIX16 "X"
|
||||
#define PRIX32 "X"
|
||||
#define PRIX64 "lX"
|
||||
|
||||
/* fprintf macros for the signed least-width types (same typedefs as the
|
||||
* exact-width types on x86_64). */
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIdLEAST16 "d"
|
||||
#define PRIdLEAST32 "d"
|
||||
#define PRIdLEAST64 "ld"
|
||||
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIiLEAST16 "i"
|
||||
#define PRIiLEAST32 "i"
|
||||
#define PRIiLEAST64 "li"
|
||||
|
||||
/* fprintf macros for the unsigned least-width types. */
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIoLEAST16 "o"
|
||||
#define PRIoLEAST32 "o"
|
||||
#define PRIoLEAST64 "lo"
|
||||
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIuLEAST16 "u"
|
||||
#define PRIuLEAST32 "u"
|
||||
#define PRIuLEAST64 "lu"
|
||||
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIxLEAST16 "x"
|
||||
#define PRIxLEAST32 "x"
|
||||
#define PRIxLEAST64 "lx"
|
||||
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIXLEAST16 "X"
|
||||
#define PRIXLEAST32 "X"
|
||||
#define PRIXLEAST64 "lX"
|
||||
|
||||
/* fprintf macros for the signed fastest types (int_fast8_t is signed char,
|
||||
* which promotes to int; int_fast16/32/64_t are long). */
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIdFAST16 "ld"
|
||||
#define PRIdFAST32 "ld"
|
||||
#define PRIdFAST64 "ld"
|
||||
|
||||
#define PRIiFAST8 "i"
|
||||
#define PRIiFAST16 "li"
|
||||
#define PRIiFAST32 "li"
|
||||
#define PRIiFAST64 "li"
|
||||
|
||||
/* fprintf macros for the unsigned fastest types. */
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIoFAST16 "lo"
|
||||
#define PRIoFAST32 "lo"
|
||||
#define PRIoFAST64 "lo"
|
||||
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIuFAST16 "lu"
|
||||
#define PRIuFAST32 "lu"
|
||||
#define PRIuFAST64 "lu"
|
||||
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIxFAST16 "lx"
|
||||
#define PRIxFAST32 "lx"
|
||||
#define PRIxFAST64 "lx"
|
||||
|
||||
#define PRIXFAST8 "X"
|
||||
#define PRIXFAST16 "lX"
|
||||
#define PRIXFAST32 "lX"
|
||||
#define PRIXFAST64 "lX"
|
||||
|
||||
/* fprintf macros for the widest (intmax_t/uintmax_t) and pointer-width
|
||||
* (intptr_t/uintptr_t) types; both families are long/unsigned long. */
|
||||
#define PRIdMAX "ld"
|
||||
#define PRIiMAX "li"
|
||||
#define PRIoMAX "lo"
|
||||
#define PRIuMAX "lu"
|
||||
#define PRIxMAX "lx"
|
||||
#define PRIXMAX "lX"
|
||||
|
||||
#define PRIdPTR "ld"
|
||||
#define PRIiPTR "li"
|
||||
#define PRIoPTR "lo"
|
||||
#define PRIuPTR "lu"
|
||||
#define PRIxPTR "lx"
|
||||
#define PRIXPTR "lX"
|
||||
|
||||
/*
|
||||
* fscanf macros for the signed exact-width types: the pointer targets the
|
||||
* exact type, so the modifier is hh/h/(none)/l by width.
|
||||
*/
|
||||
#define SCNd8 "hhd"
|
||||
#define SCNd16 "hd"
|
||||
#define SCNd32 "d"
|
||||
#define SCNd64 "ld"
|
||||
|
||||
#define SCNi8 "hhi"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNi32 "i"
|
||||
#define SCNi64 "li"
|
||||
|
||||
/* fscanf macros for the unsigned exact-width types. */
|
||||
#define SCNo8 "hho"
|
||||
#define SCNo16 "ho"
|
||||
#define SCNo32 "o"
|
||||
#define SCNo64 "lo"
|
||||
|
||||
#define SCNu8 "hhu"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNu32 "u"
|
||||
#define SCNu64 "lu"
|
||||
|
||||
#define SCNx8 "hhx"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNx32 "x"
|
||||
#define SCNx64 "lx"
|
||||
|
||||
/* fscanf macros for the signed least-width types (same typedefs as the
|
||||
* exact-width types on x86_64). */
|
||||
#define SCNdLEAST8 "hhd"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNdLEAST32 "d"
|
||||
#define SCNdLEAST64 "ld"
|
||||
|
||||
#define SCNiLEAST8 "hhi"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNiLEAST32 "i"
|
||||
#define SCNiLEAST64 "li"
|
||||
|
||||
/* fscanf macros for the unsigned least-width types. */
|
||||
#define SCNoLEAST8 "hho"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNoLEAST32 "o"
|
||||
#define SCNoLEAST64 "lo"
|
||||
|
||||
#define SCNuLEAST8 "hhu"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNuLEAST32 "u"
|
||||
#define SCNuLEAST64 "lu"
|
||||
|
||||
#define SCNxLEAST8 "hhx"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNxLEAST32 "x"
|
||||
#define SCNxLEAST64 "lx"
|
||||
|
||||
/* fscanf macros for the signed fastest types (int_fast8_t is signed char;
|
||||
* int_fast16/32/64_t are long). */
|
||||
#define SCNdFAST8 "hhd"
|
||||
#define SCNdFAST16 "ld"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNdFAST64 "ld"
|
||||
|
||||
#define SCNiFAST8 "hhi"
|
||||
#define SCNiFAST16 "li"
|
||||
#define SCNiFAST32 "li"
|
||||
#define SCNiFAST64 "li"
|
||||
|
||||
/* fscanf macros for the unsigned fastest types. */
|
||||
#define SCNoFAST8 "hho"
|
||||
#define SCNoFAST16 "lo"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNoFAST64 "lo"
|
||||
|
||||
#define SCNuFAST8 "hhu"
|
||||
#define SCNuFAST16 "lu"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNuFAST64 "lu"
|
||||
|
||||
#define SCNxFAST8 "hhx"
|
||||
#define SCNxFAST16 "lx"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNxFAST64 "lx"
|
||||
|
||||
/* fscanf macros for the widest and pointer-width types. */
|
||||
#define SCNdMAX "ld"
|
||||
#define SCNiMAX "li"
|
||||
#define SCNoMAX "lo"
|
||||
#define SCNuMAX "lu"
|
||||
#define SCNxMAX "lx"
|
||||
|
||||
#define SCNdPTR "ld"
|
||||
#define SCNiPTR "li"
|
||||
#define SCNoPTR "lo"
|
||||
#define SCNuPTR "lu"
|
||||
#define SCNxPTR "lx"
|
||||
|
||||
/*
|
||||
* Return the absolute value of n. abs(INTMAX_MIN) is not representable;
|
||||
* it wraps (implementation-defined, no errno), matching GCC's two's
|
||||
* complement semantics.
|
||||
* const: result depends only on the argument (GCC predeclares imaxabs
|
||||
* with this attribute, so no weaker one may be used).
|
||||
*/
|
||||
__attribute__((const)) intmax_t
|
||||
imaxabs(intmax_t n);
|
||||
|
||||
/*
|
||||
* Return { quot, rem } of numer / denom, both truncated toward zero with
|
||||
* the remainder taking the sign of numer (C division semantics).
|
||||
* const: result depends only on the arguments.
|
||||
*/
|
||||
__attribute__((const)) imaxdiv_t
|
||||
imaxdiv(intmax_t numer, intmax_t denom);
|
||||
|
||||
/*
|
||||
* Convert the initial portion of nptr to intmax_t, as strtol with the
|
||||
* widest signed type. intmax_t is long on x86_64 LP64, so this is an
|
||||
* ABI-identical delegation to strtol (same engine, same errno policy:
|
||||
* ERANGE clamps, EINVAL for a base outside {0} union [2, 36]).
|
||||
*/
|
||||
intmax_t
|
||||
strtoimax(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/* As strtoimax, converted to uintmax_t (unsigned long); negative subject
|
||||
* sequences wrap modulo 2^64 and are never a range error (C23 7.8.2.3). */
|
||||
uintmax_t
|
||||
strtoumax(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* Wide-character forms of strtoimax/strtoumax (C23 7.8.2.4-5). The subject
|
||||
* sequence is scanned from nptr under the same rules as strtol: C-locale
|
||||
* whitespace (L' ' and L'\t'..L'\r'), optional sign, base 0/2..36 with the
|
||||
* 0x/0 prefix rules, ASCII digits only. errno and endptr (of type
|
||||
* wchar_t **) behave exactly as the narrow forms.
|
||||
*/
|
||||
intmax_t
|
||||
wcstoimax(const wchar_t *restrict nptr, wchar_t **restrict endptr, int base);
|
||||
|
||||
uintmax_t
|
||||
wcstoumax(const wchar_t *restrict nptr, wchar_t **restrict endptr, int base);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_INTTYPES_H */
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef VLIBC_ISO646_H
|
||||
#define VLIBC_ISO646_H
|
||||
|
||||
/*
|
||||
* vlibc — <iso646.h>.
|
||||
*
|
||||
* Alternative spellings for the bitwise and logical operators. C23 removed
|
||||
* them from the language as alternative tokens, so this header provides them
|
||||
* as macros; in C17 the spellings are lexed as tokens and the macros never
|
||||
* fire, which makes the definitions harmless in both modes. In C++ they are
|
||||
* part of the language, so no macros are defined there.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define and &&
|
||||
#define and_eq &=
|
||||
#define bitand &
|
||||
#define bitor |
|
||||
#define compl ~
|
||||
#define not !
|
||||
#define not_eq !=
|
||||
#define or ||
|
||||
#define or_eq |=
|
||||
#define xor ^
|
||||
#define xor_eq ^=
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_ISO646_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef VLIBC_LIBGEN_H
|
||||
#define VLIBC_LIBGEN_H
|
||||
|
||||
/*
|
||||
* vlibc — <libgen.h>.
|
||||
*
|
||||
* basename() and dirname() split a pathname into its final component and
|
||||
* the directory that contains it (XSI, declared when _XOPEN_SOURCE is
|
||||
* enabled in glibc — level 2 here). They are the XSI forms: the input
|
||||
* path is modified in place (callers must pass a writable buffer), and
|
||||
* both are allowed to return pointers into a shared static area, so
|
||||
* repeated calls overwrite earlier results.
|
||||
*
|
||||
* The two functions are deliberately declared here and nowhere else:
|
||||
* POSIX places them in <libgen.h> only.
|
||||
*
|
||||
* Neither declaration carries an intent attribute: both functions write
|
||||
* to and return pointers into their argument or a shared static buffer,
|
||||
* so const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Return a pointer to the final component of path, modifying path in
|
||||
* place: trailing '/' characters (other than a leading root '/') are
|
||||
* overwritten with NUL so basename("/usr/lib") == "lib", basename("/")
|
||||
* == "/" and basename("") == ".". The result may be a suffix of path or
|
||||
* point into a shared static buffer (POSIX permits either); the special
|
||||
* cases "/" and "" return static strings.
|
||||
*/
|
||||
char *
|
||||
basename(char *path);
|
||||
|
||||
/*
|
||||
* Return a pointer to the directory portion of path, modifying path in
|
||||
* place: the last '/' that is not the final character is overwritten
|
||||
* with NUL so dirname("/usr/lib") == "/usr", dirname("/") == "/" and
|
||||
* dirname("") == ".". The result may be a suffix of path or point into
|
||||
* a shared static buffer (POSIX permits either); the special cases
|
||||
* return static strings.
|
||||
*/
|
||||
char *
|
||||
dirname(char *path);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LIBGEN_H */
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef VLIBC_LIMITS_H
|
||||
#define VLIBC_LIMITS_H
|
||||
|
||||
/*
|
||||
* vlibc — <limits.h>.
|
||||
*
|
||||
* Sizes of integer types (C23). Every value is derived from compiler
|
||||
* predefined macros (__CHAR_BIT__, __SCHAR_MAX__, __SIZEOF_INT__, ...), so
|
||||
* this header is fully self-contained. char is signed on x86_64; the
|
||||
* __CHAR_UNSIGNED__ branch keeps the header correct if that ever changes.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
/* Number of bits in the smallest addressable object. */
|
||||
#define CHAR_BIT __CHAR_BIT__
|
||||
|
||||
/* Maximum number of bytes in a multibyte character (UTF-8 over 32-bit
|
||||
* wchar_t needs at most 4). */
|
||||
#define MB_LEN_MAX 4
|
||||
|
||||
/* Limits of the plain char types. */
|
||||
#define SCHAR_MIN (-__SCHAR_MAX__ - 1)
|
||||
#define SCHAR_MAX __SCHAR_MAX__
|
||||
#define UCHAR_MAX (__SCHAR_MAX__ * 2 + 1)
|
||||
#if defined(__CHAR_UNSIGNED__)
|
||||
#define CHAR_MIN 0
|
||||
#define CHAR_MAX UCHAR_MAX
|
||||
#else
|
||||
#define CHAR_MIN SCHAR_MIN
|
||||
#define CHAR_MAX SCHAR_MAX
|
||||
#endif
|
||||
|
||||
/* Limits of the short types. */
|
||||
#define SHRT_MIN (-__SHRT_MAX__ - 1)
|
||||
#define SHRT_MAX __SHRT_MAX__
|
||||
#define USHRT_MAX (__SHRT_MAX__ * 2 + 1)
|
||||
|
||||
/* Limits of the int types. */
|
||||
#define INT_MIN (-__INT_MAX__ - 1)
|
||||
#define INT_MAX __INT_MAX__
|
||||
#define UINT_MAX (__INT_MAX__ * 2U + 1U)
|
||||
|
||||
/* Limits of the long types (x86_64 LP64: 64-bit). */
|
||||
#define LONG_MIN (-__LONG_MAX__ - 1L)
|
||||
#define LONG_MAX __LONG_MAX__
|
||||
#define ULONG_MAX (__LONG_MAX__ * 2UL + 1UL)
|
||||
|
||||
/* Limits of the long long types. */
|
||||
#define LLONG_MIN (-__LONG_LONG_MAX__ - 1LL)
|
||||
#define LLONG_MAX __LONG_LONG_MAX__
|
||||
#define ULLONG_MAX (__LONG_LONG_MAX__ * 2ULL + 1ULL)
|
||||
|
||||
/* Width of each integer type, in bits (C23). */
|
||||
#define BOOL_WIDTH 1
|
||||
#define CHAR_WIDTH __CHAR_BIT__
|
||||
#define SCHAR_WIDTH __CHAR_BIT__
|
||||
#define UCHAR_WIDTH __CHAR_BIT__
|
||||
#define SHRT_WIDTH (__SIZEOF_SHORT__ * __CHAR_BIT__)
|
||||
#define USHRT_WIDTH (__SIZEOF_SHORT__ * __CHAR_BIT__)
|
||||
#define INT_WIDTH (__SIZEOF_INT__ * __CHAR_BIT__)
|
||||
#define UINT_WIDTH (__SIZEOF_INT__ * __CHAR_BIT__)
|
||||
#define LONG_WIDTH (__SIZEOF_LONG__ * __CHAR_BIT__)
|
||||
#define ULONG_WIDTH (__SIZEOF_LONG__ * __CHAR_BIT__)
|
||||
#define LLONG_WIDTH (__SIZEOF_LONG_LONG__ * __CHAR_BIT__)
|
||||
#define ULLONG_WIDTH (__SIZEOF_LONG_LONG__ * __CHAR_BIT__)
|
||||
|
||||
#endif /* VLIBC_LIMITS_H */
|
||||
+634
@@ -0,0 +1,634 @@
|
||||
#ifndef VLIBC_MATH_H
|
||||
#define VLIBC_MATH_H
|
||||
|
||||
/*
|
||||
* vlibc — <math.h>.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile. It carries the
|
||||
* whole todo-39 arithmetic inventory: the classification macros
|
||||
* (fpclassify/isnan/isinf/isfinite/isnormal/signbit), the constants
|
||||
* (HUGE_VAL/INFINITY/NAN, FP_*, math_errhandling), and the 25 basic
|
||||
* real-function families (fabs, copysign, floor, ceil, trunc, round, rint,
|
||||
* nearbyint, lrint, llrint, frexp, ldexp, modf, scalbn, scalbln, fmin,
|
||||
* fmax, fdim, fmod, remainder, remquo, ilogb, logb, lround, llround), each
|
||||
* spelled in all three precisions (float `...f`, double, long double
|
||||
* `...l`).
|
||||
*
|
||||
* Every declaration below lands NOW so that the header is stable across the
|
||||
* todo-39 implementation slices; the slices (wip commits) fill in the
|
||||
* src/math/ definitions family by family and never touch this header again.
|
||||
* Later math todos extend this header in place the same way stdlib.h is
|
||||
* extended: todo 40 adds the exp/log/pow families, todo 41 the trig and
|
||||
* hyperbolic families, todo 42 erf/erfc/lgamma/tgamma/fma/nextafter/nan,
|
||||
* and todo 43 provides <complex.h> separately.
|
||||
*
|
||||
* Edge-case behavior (IEEE 754-2008): floor/ceil/trunc round exact results
|
||||
* with correct signed-zero and NaN/Inf handling; fabs/copysign/fmin/fmax/
|
||||
* fdim never raise an exception on their own and preserve NaN payloads
|
||||
* where the standard allows; the classification macros are
|
||||
* exception-free. Functions that per POSIX can set errno (ldexp, scalbn,
|
||||
* scalbln, fmod, remainder, remquo, and the fraction-splitters frexp/modf
|
||||
* with their pointer outputs) carry no const attribute below so the
|
||||
* compiler never hoists or elides an errno-setting call. Functions that
|
||||
* are pure (never set errno in their defined domain — fabs, copysign,
|
||||
* floor, ceil, trunc, round, rint, nearbyint, fmin, fmax, fdim, ilogb,
|
||||
* logb) are declared const and fold away in static links.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Constants ---- */
|
||||
|
||||
/*
|
||||
* Positive infinity, as double/float/long double (C23 7.12.1p4). HUGE_VAL
|
||||
* is also the "overflowed" return value of the strto* conversions.
|
||||
*/
|
||||
#define HUGE_VAL __builtin_huge_val()
|
||||
#define HUGE_VALF __builtin_huge_valf()
|
||||
#define HUGE_VALL __builtin_huge_vall()
|
||||
|
||||
/* Positive infinity as a float (C23 7.12.1p5). */
|
||||
#define INFINITY __builtin_inff()
|
||||
|
||||
/* A quiet NaN as a float (C23 7.12.1p6); the payload is implementation
|
||||
* defined but always a NaN of the quiet kind. */
|
||||
#define NAN __builtin_nanf("")
|
||||
|
||||
/*
|
||||
* Return values of fpclassify (C23 7.12.3.1): the numbers need only be
|
||||
* distinct positive values; the classification macros below hand these
|
||||
* exact constants to __builtin_fpclassify so the two always agree.
|
||||
*/
|
||||
#define FP_NAN 0
|
||||
#define FP_INFINITE 1
|
||||
#define FP_ZERO 2
|
||||
#define FP_SUBNORMAL 3
|
||||
#define FP_NORMAL 4
|
||||
|
||||
/*
|
||||
* Return values of ilogb (C23 7.12.6.5p4): FP_ILOGB0 for a zero argument
|
||||
* and FP_ILOGBNAN for a NaN or infinite argument, each the indicated
|
||||
* <limits.h> sentinel.
|
||||
*/
|
||||
#define FP_ILOGB0 INT_MIN
|
||||
#define FP_ILOGBNAN INT_MAX
|
||||
|
||||
/*
|
||||
* Which error mechanisms the library reports through (C23 7.12.1p3):
|
||||
* MATH_ERRNO and MATH_ERREXCEPT are both in effect (the domain/range
|
||||
* errno values are set where POSIX requires and the corresponding
|
||||
* floating-point exceptions are raised by the hardware).
|
||||
*/
|
||||
#define MATH_ERRNO 1
|
||||
#define MATH_ERREXCEPT 2
|
||||
#define math_errhandling 3
|
||||
|
||||
/* ---- Classification macros (type-generic, exception-free) ---- */
|
||||
|
||||
/*
|
||||
* Classify x as NaN, infinite, zero, subnormal, or normal, returning the
|
||||
* matching FP_* constant. __builtin_fpclassify is a GCC type-generic
|
||||
* builtin that evaluates its floating argument exactly once and never
|
||||
* traps, so no _Generic dispatch is needed here.
|
||||
*/
|
||||
#define fpclassify(x) \
|
||||
__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, (x))
|
||||
|
||||
/*
|
||||
* The predicate macros below are thin wrappers over the corresponding
|
||||
* GCC type-generic builtins. Each builtin evaluates its argument once and
|
||||
* returns an int; integer arguments classify as finite, non-zero, and
|
||||
* normal (they convert exactly), so isnan(3) is 0 and isinf(3) is 0.
|
||||
*/
|
||||
#define isnan(x) __builtin_isnan(x)
|
||||
#define isinf(x) __builtin_isinf(x)
|
||||
#define isfinite(x) __builtin_isfinite(x)
|
||||
#define isnormal(x) __builtin_isnormal(x)
|
||||
#define signbit(x) __builtin_signbit(x)
|
||||
|
||||
/* ---- Basic arithmetic families (todo 39) ---- */
|
||||
|
||||
/*
|
||||
* Absolute value of x (C23 7.12.7.2). fabs(±0) is +0, fabs(±Inf) is +Inf,
|
||||
* and fabs(NaN) is a NaN. Pure: no domain, no exception.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fabsf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
fabs(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fabsl(long double x);
|
||||
|
||||
/*
|
||||
* A value with the magnitude of x and the sign of y (C23 7.12.7.3).
|
||||
* copysign(±0, y) carries y's sign; a NaN x keeps its payload but takes
|
||||
* y's sign bit. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
copysignf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
copysign(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
copysignl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The largest integral value not greater than x (C23 7.12.9.2).
|
||||
* floor(±0) is ±0, floor(-0.5) is -1.0, floor(±Inf) and floor(NaN)
|
||||
* return their argument unchanged. Pure and exact (no inexact
|
||||
* exception), so it folds.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
floorf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
floor(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
floorl(long double x);
|
||||
|
||||
/*
|
||||
* The smallest integral value not less than x (C23 7.12.9.1).
|
||||
* ceil(±0) is ±0, ceil(-0.5) is -0.0 (a real negative zero), ceil(±Inf)
|
||||
* and ceil(NaN) return their argument unchanged. Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
ceilf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
ceil(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
ceill(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x in the direction of zero (C23 7.12.9.3).
|
||||
* trunc(-0.7) is -0.0, trunc(0.7) is 0.0, and ±Inf/NaN pass through.
|
||||
* Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
truncf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
trunc(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
truncl(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x, with halfway cases rounded away from
|
||||
* zero (C23 7.12.9.6). round(±0) is ±0, round(-0.5) is -1.0, and ±Inf/
|
||||
* NaN pass through. Pure: round never raises the inexact exception.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
roundf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
round(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
roundl(long double x);
|
||||
|
||||
/*
|
||||
* The integral value nearest to x in the current rounding direction
|
||||
* (C23 7.12.9.4). rint may raise the inexact exception; it is still pure
|
||||
* in the const sense because it never sets errno and reads no memory.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
rintf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
rint(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
rintl(long double x);
|
||||
|
||||
/*
|
||||
* As rint, but guaranteed never to raise the inexact exception (C23
|
||||
* 7.12.9.5). Pure and exact.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
nearbyintf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
nearbyint(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
nearbyintl(long double x);
|
||||
|
||||
/*
|
||||
* The nearest integral value to x in the current rounding direction,
|
||||
* returned as long (C23 7.12.9.7). A result outside the range of long is
|
||||
* a range error (the return value is unspecified and errno may be set),
|
||||
* so no const attribute.
|
||||
*/
|
||||
long
|
||||
lrintf(float x);
|
||||
|
||||
long
|
||||
lrint(double x);
|
||||
|
||||
long
|
||||
lrintl(long double x);
|
||||
|
||||
/*
|
||||
* As lrint, returned as long long (C23 7.12.9.8). Range errors as for
|
||||
* lrint; no const attribute.
|
||||
*/
|
||||
long long
|
||||
llrintf(float x);
|
||||
|
||||
long long
|
||||
llrint(double x);
|
||||
|
||||
long long
|
||||
llrintl(long double x);
|
||||
|
||||
/*
|
||||
* Split x into a fraction f in [1/2, 1) (or 0) and an integer exponent
|
||||
* *exp such that x == f * 2^(*exp) (C23 7.12.6.4). Zero returns ±0 with
|
||||
* *exp 0; Inf/NaN return x with an unspecified *exp. Writes *exp, so no
|
||||
* const attribute.
|
||||
*/
|
||||
float
|
||||
frexpf(float x, int *exp);
|
||||
|
||||
double
|
||||
frexp(double x, int *exp);
|
||||
|
||||
long double
|
||||
frexpl(long double x, int *exp);
|
||||
|
||||
/*
|
||||
* x times 2^n (C23 7.12.6.3): the inverse of frexp. A result too large
|
||||
* to represent is a range error returning ±HUGE_VAL with errno ERANGE;
|
||||
* no const attribute.
|
||||
*/
|
||||
float
|
||||
ldexpf(float x, int n);
|
||||
|
||||
double
|
||||
ldexp(double x, int n);
|
||||
|
||||
long double
|
||||
ldexpl(long double x, int n);
|
||||
|
||||
/*
|
||||
* Split x into an integral part stored in *iptr and a fractional part
|
||||
* returned (C23 7.12.6.5); both have x's sign, so modf(-1.5, &i) puts
|
||||
* -1.0 in i and returns -0.5. Writes *iptr, so no const attribute.
|
||||
*/
|
||||
float
|
||||
modff(float x, float *iptr);
|
||||
|
||||
double
|
||||
modf(double x, double *iptr);
|
||||
|
||||
long double
|
||||
modfl(long double x, long double *iptr);
|
||||
|
||||
/*
|
||||
* x * FLT_RADIX^n with FLT_RADIX 2 (C23 7.12.6.6): the scalb* functions
|
||||
* differ from ldexp only in the exponent argument's type. A result too
|
||||
* large to represent is a range error returning ±HUGE_VAL* with errno
|
||||
* ERANGE; no const attribute.
|
||||
*/
|
||||
float
|
||||
scalbnf(float x, int n);
|
||||
|
||||
double
|
||||
scalbn(double x, int n);
|
||||
|
||||
long double
|
||||
scalbnl(long double x, int n);
|
||||
|
||||
float
|
||||
scalblnf(float x, long n);
|
||||
|
||||
double
|
||||
scalbln(double x, long n);
|
||||
|
||||
long double
|
||||
scalblnl(long double x, long n);
|
||||
|
||||
/*
|
||||
* The smaller of x and y (C23 7.12.12.4), returning -0.0 when the
|
||||
* arguments are +0.0 and -0.0. A NaN argument is ignored in favor of the
|
||||
* numeric one; two NaNs return a NaN. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fminf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fmin(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fminl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The larger of x and y (C23 7.12.12.3), returning +0.0 when the
|
||||
* arguments are +0.0 and -0.0. NaN handling as fmin. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fmaxf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fmax(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fmaxl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The positive difference x - y when x > y and +0.0 otherwise (C23
|
||||
* 7.12.12.2); fdim(x, NaN) and fdim(NaN, x) return a NaN. Pure in the
|
||||
* domain-error-free sense (an overflow may still raise an exception
|
||||
* through the hardware, which const does not model).
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
fdimf(float x, float y);
|
||||
|
||||
__attribute__((const)) double
|
||||
fdim(double x, double y);
|
||||
|
||||
__attribute__((const)) long double
|
||||
fdiml(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The floating-point remainder x - n*y, where n is x/y truncated toward
|
||||
* zero (C23 7.12.10.1); the result therefore has x's sign. fmod(x, ±0) is
|
||||
* a domain error returning NaN with errno EDOM; ±Inf/x is likewise a
|
||||
* domain error. errno can be set, so no const attribute.
|
||||
*/
|
||||
float
|
||||
fmodf(float x, float y);
|
||||
|
||||
double
|
||||
fmod(double x, double y);
|
||||
|
||||
long double
|
||||
fmodl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* The IEEE remainder x - n*y, where n is x/y rounded to the nearest
|
||||
* integer (ties to even) (C23 7.12.10.2); |result| <= |y|/2. Domain
|
||||
* errors as fmod; errno can be set, so no const attribute.
|
||||
*/
|
||||
float
|
||||
remainderf(float x, float y);
|
||||
|
||||
double
|
||||
remainder(double x, double y);
|
||||
|
||||
long double
|
||||
remainderl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* As remainder, additionally storing the low bits of the integer
|
||||
* quotient n in *quo (C23 7.12.10.3). Writes *quo, so no const
|
||||
* attribute.
|
||||
*/
|
||||
float
|
||||
remquof(float x, float y, int *quo);
|
||||
|
||||
double
|
||||
remquo(double x, double y, int *quo);
|
||||
|
||||
long double
|
||||
remquol(long double x, long double y, int *quo);
|
||||
|
||||
/*
|
||||
* The signed exponent of x as an int (C23 7.12.6.5p1): ilogb(x) is
|
||||
* floor(log2 |x|) for a nonzero finite x. ilogb(±0) returns FP_ILOGB0,
|
||||
* ilogb(±Inf) and ilogb(NaN) return FP_ILOGBNAN. Pure: the sentinels are
|
||||
* returned without touching errno.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
ilogbf(float x);
|
||||
|
||||
__attribute__((const)) int
|
||||
ilogb(double x);
|
||||
|
||||
__attribute__((const)) int
|
||||
ilogbl(long double x);
|
||||
|
||||
/*
|
||||
* The signed exponent of x as a floating-point value (C23 7.12.6.6):
|
||||
* logb(±0) is -Inf and logb(±Inf) is +Inf, each raising the division-
|
||||
* by-zero/invalid exception through the hardware but without an errno
|
||||
* path in the representable domain. Pure.
|
||||
*/
|
||||
__attribute__((const)) float
|
||||
logbf(float x);
|
||||
|
||||
__attribute__((const)) double
|
||||
logb(double x);
|
||||
|
||||
__attribute__((const)) long double
|
||||
logbl(long double x);
|
||||
|
||||
/*
|
||||
* The nearest integral value to x, with halfway cases rounded away from
|
||||
* zero, returned as long (C23 7.12.9.9). A result outside the range of
|
||||
* long is a range error; no const attribute.
|
||||
*/
|
||||
long
|
||||
lroundf(float x);
|
||||
|
||||
long
|
||||
lround(double x);
|
||||
|
||||
long
|
||||
lroundl(long double x);
|
||||
|
||||
/*
|
||||
* As lround, returned as long long (C23 7.12.9.10). Range errors as for
|
||||
* lround; no const attribute.
|
||||
*/
|
||||
long long
|
||||
llroundf(float x);
|
||||
|
||||
long long
|
||||
llround(double x);
|
||||
|
||||
long long
|
||||
llroundl(long double x);
|
||||
|
||||
/*
|
||||
* e raised to the power x (C23 7.12.6.1). exp(+0) == exp(-0) == 1;
|
||||
* exp(-Inf) == +0; exp(+Inf) == +Inf; exp(NaN) == NaN. A result too
|
||||
* large or too small is a range error and sets errno to ERANGE, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
exp(double x);
|
||||
|
||||
float
|
||||
expf(float x);
|
||||
|
||||
long double
|
||||
expl(long double x);
|
||||
|
||||
/*
|
||||
* 2 raised to the power x (C23 7.12.6.2). exp2(+0) == exp2(-0) == 1;
|
||||
* exp2(-Inf) == +0; exp2(+Inf) == +Inf; exp2(NaN) == NaN. A result too
|
||||
* large or too small is a range error and sets errno to ERANGE, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
exp2(double x);
|
||||
|
||||
float
|
||||
exp2f(float x);
|
||||
|
||||
long double
|
||||
exp2l(long double x);
|
||||
|
||||
/*
|
||||
* e raised to the power x minus 1 (C23 7.12.6.3). expm1(+0) == +0;
|
||||
* expm1(-0) == -0; expm1(-Inf) == -1; expm1(+Inf) == +Inf;
|
||||
* expm1(NaN) == NaN. A result too large is a range error and sets
|
||||
* errno to ERANGE, so no const attribute.
|
||||
*/
|
||||
double
|
||||
expm1(double x);
|
||||
|
||||
float
|
||||
expm1f(float x);
|
||||
|
||||
long double
|
||||
expm1l(long double x);
|
||||
|
||||
/*
|
||||
* Natural logarithm of x (C23 7.12.6.7). log(+0) == log(-0) == -Inf;
|
||||
* log(1) == 0; log(-x) == NaN (domain error); log(+Inf) == +Inf;
|
||||
* log(NaN) == NaN. A negative argument is a domain error setting errno
|
||||
* to EDOM and a zero argument is a range error setting errno to ERANGE,
|
||||
* so no const attribute.
|
||||
*/
|
||||
double
|
||||
log(double x);
|
||||
|
||||
float
|
||||
logf(float x);
|
||||
|
||||
long double
|
||||
logl(long double x);
|
||||
|
||||
/*
|
||||
* Base-2 logarithm of x (C23 7.12.6.8). log2(1) == 0; log2(2) == 1.
|
||||
* Special values, domain errors, and range errors as for log, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
log2(double x);
|
||||
|
||||
float
|
||||
log2f(float x);
|
||||
|
||||
long double
|
||||
log2l(long double x);
|
||||
|
||||
/*
|
||||
* Base-10 logarithm of x (C23 7.12.6.9). log10(1) == 0; log10(10) == 1.
|
||||
* Special values, domain errors, and range errors as for log, so no
|
||||
* const attribute.
|
||||
*/
|
||||
double
|
||||
log10(double x);
|
||||
|
||||
float
|
||||
log10f(float x);
|
||||
|
||||
long double
|
||||
log10l(long double x);
|
||||
|
||||
/*
|
||||
* Natural logarithm of 1 + x (C23 7.12.6.13). log1p(+-0) == +-0;
|
||||
* log1p(-1) == -Inf (a pole error setting errno to ERANGE); log1p(x) for
|
||||
* x < -1 is a domain error setting errno to EDOM; log1p(+Inf) == +Inf;
|
||||
* log1p(NaN) == NaN. Accurate for x close to 0, so no const attribute.
|
||||
*/
|
||||
double
|
||||
log1p(double x);
|
||||
|
||||
float
|
||||
log1pf(float x);
|
||||
|
||||
long double
|
||||
log1pl(long double x);
|
||||
|
||||
/*
|
||||
* Principal square root of x (C23 7.12.5.4). sqrt(+-0) == +-0; sqrt(x)
|
||||
* for x < 0 is a domain error returning NaN and setting errno to EDOM;
|
||||
* sqrt(+Inf) == +Inf; sqrt(NaN) == NaN. Correctly rounded; no const
|
||||
* attribute because of the errno path.
|
||||
*/
|
||||
double
|
||||
sqrt(double x);
|
||||
|
||||
float
|
||||
sqrtf(float x);
|
||||
|
||||
long double
|
||||
sqrtl(long double x);
|
||||
|
||||
/*
|
||||
* Cube root of x (C23 7.12.5.5). cbrt(+-0) == +-0; cbrt(+-Inf) == +-Inf;
|
||||
* cbrt(NaN) == NaN; cbrt(-x) == -cbrt(x). Defined for all reals and never
|
||||
* sets errno.
|
||||
*/
|
||||
double
|
||||
cbrt(double x);
|
||||
|
||||
float
|
||||
cbrtf(float x);
|
||||
|
||||
long double
|
||||
cbrtl(long double x);
|
||||
|
||||
/*
|
||||
* Square root of x*x + y*y without undue overflow or underflow
|
||||
* (C23 7.12.5.6). hypot(+-0, +-0) == +0; hypot(+-Inf, y) == +Inf even for
|
||||
* NaN y; hypot(x, NaN) == NaN for finite x. The internal scaling avoids
|
||||
* spurious overflow, so no errno path exists.
|
||||
*/
|
||||
double
|
||||
hypot(double x, double y);
|
||||
|
||||
float
|
||||
hypotf(float x, float y);
|
||||
|
||||
long double
|
||||
hypotl(long double x, long double y);
|
||||
|
||||
/*
|
||||
* x raised to the power y (C23 7.12.6.11). pow(x, 0) == 1; pow(1, y) == 1
|
||||
* even for NaN y; pow(NaN, 0) == 1; pow(+-0, negative y) == +-Inf (a pole
|
||||
* error setting errno to ERANGE); pow(negative finite x, non-integer y) is
|
||||
* a domain error setting errno to EDOM; overflow and underflow set errno to
|
||||
* ERANGE. No const attribute.
|
||||
*/
|
||||
double
|
||||
pow(double x, double y);
|
||||
|
||||
float
|
||||
powf(float x, float y);
|
||||
|
||||
long double
|
||||
powl(long double x, long double y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_MATH_H */
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifndef VLIBC_POLL_H
|
||||
#define VLIBC_POLL_H
|
||||
|
||||
/*
|
||||
* vlibc — <poll.h>.
|
||||
*
|
||||
* Poll multiple file descriptors for readiness. Every declaration here is a
|
||||
* thin pass-through to the kernel: poll() blocks per its millisecond
|
||||
* timeout, ppoll() takes a struct timespec and an optional signal mask
|
||||
* directly, and both return the count of ready descriptors (0 on timeout,
|
||||
* -1 with errno set on error).
|
||||
*
|
||||
* Level 1 (onlyposix): poll, nfds_t, struct pollfd, the POLL* event bits.
|
||||
* Level 2 (muslmimic): ppoll (Linux-specific; POSIX has only poll).
|
||||
*
|
||||
* struct pollfd and the POLL* values mirror the kernel ABI (x86_64
|
||||
* asm-generic/poll.h) exactly: the wrappers pass them to SYS_ppoll
|
||||
* unmodified. POLLERR, POLLHUP and POLLNVAL are report-only — they can
|
||||
* appear in revents even when not requested in events.
|
||||
*
|
||||
* The signal mask type ppoll takes is declared under the shared guard
|
||||
* below; <signal.h> is its canonical POSIX home and will own the full sig*
|
||||
* API.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Requestable event bits (poll()'s events field). */
|
||||
#define POLLIN 0x001 /* readable */
|
||||
#define POLLPRI 0x002 /* urgent readable (out-of-band) */
|
||||
#define POLLOUT 0x004 /* writable */
|
||||
|
||||
/* Report-only event bits (poll()'s revents field). */
|
||||
#define POLLERR 0x008 /* error condition */
|
||||
#define POLLHUP 0x010 /* hung up */
|
||||
#define POLLNVAL 0x020 /* invalid fd */
|
||||
|
||||
/* The number of struct pollfd entries poll()/ppoll() watch. */
|
||||
typedef unsigned long nfds_t;
|
||||
|
||||
/*
|
||||
* One descriptor watched by poll()/ppoll(). fd is the descriptor (negative
|
||||
* to ignore), events the requested bits (POLLIN/POLLOUT), revents the bits
|
||||
* the kernel reports (the requested bits plus any POLLERR/POLLHUP/POLLNVAL).
|
||||
*/
|
||||
struct pollfd
|
||||
{
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wait for readiness on the first nfds entries of fds, blocking up to
|
||||
* timeout milliseconds (timeout < 0 blocks indefinitely, 0 never blocks).
|
||||
* Return the number of entries with a nonzero revents, 0 on timeout, or -1
|
||||
* with errno set on error.
|
||||
*/
|
||||
int
|
||||
poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): Linux-specific. */
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t (see
|
||||
* setjmp.h). <signal.h> is the canonical POSIX home for sigset_t and builds
|
||||
* the sig* API on this same layout; the typedef is repeated in
|
||||
* <sys/select.h> under this guard so the two headers stay consistent.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Like poll(), but the timeout is a struct timespec and, when sigmask is
|
||||
* not NULL, the given signal mask is atomically installed for the duration
|
||||
* of the wait (the previous mask is restored before returning).
|
||||
* Linux-specific.
|
||||
*/
|
||||
int
|
||||
ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_POLL_H */
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef VLIBC_PWD_H
|
||||
#define VLIBC_PWD_H
|
||||
|
||||
/*
|
||||
* vlibc — <pwd.h>.
|
||||
*
|
||||
* The password database (todo 37). struct passwd mirrors /etc/passwd, whose
|
||||
* records are name:passwd:uid:gid:gecos:dir:shell — seven ':'-separated
|
||||
* fields. Everything here is POSIX.1-2008 base (Level 1).
|
||||
*
|
||||
* The non-reentrant forms getpwnam/getpwuid/getpwent return a pointer to a
|
||||
* static structure whose contents — including the strings it points at —
|
||||
* are overwritten by the next call to any of the three, so the result must
|
||||
* be copied out before the next database call. getpwnam/getpwuid open and
|
||||
* scan the whole file per call; getpwent walks a cursor that setpwent
|
||||
* rewinds and endpwent closes.
|
||||
*
|
||||
* The getpwnam_r/getpwuid_r/getpwent_r forms write the entry into
|
||||
* caller-supplied storage (struct, char buffer of bufsize bytes) and
|
||||
* return 0 on success with *result pointing at the filled struct, or an
|
||||
* error number as the return value with *result left NULL: ERANGE when the
|
||||
* buffer is too small for the entry, 0 with *result NULL when no entry
|
||||
* matches (or the stream is exhausted). errno is never touched by any _r
|
||||
* function: the error number IS the return value.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* One password database entry (/etc/passwd record). */
|
||||
struct passwd
|
||||
{
|
||||
char *pw_name; /* user's login name */
|
||||
char *pw_passwd; /* encrypted password; often "x" with shadow files */
|
||||
uid_t pw_uid; /* numeric user id */
|
||||
gid_t pw_gid; /* numeric group id */
|
||||
char *pw_gecos; /* user information (comment) field */
|
||||
char *pw_dir; /* home directory */
|
||||
char *pw_shell; /* login shell */
|
||||
};
|
||||
|
||||
/*
|
||||
* Look up the first entry whose name matches; NULL when absent. The result
|
||||
* points into shared static storage valid only until the next pwd call.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwnam(const char *name);
|
||||
|
||||
/*
|
||||
* Look up the first entry whose uid matches; NULL when absent. Result
|
||||
* storage is shared with getpwnam/getpwent as described above.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwuid(uid_t uid);
|
||||
|
||||
/*
|
||||
* Return the next entry from the password stream, opening it on the first
|
||||
* call; NULL at end of file. The stream cursor advances per call.
|
||||
*/
|
||||
struct passwd *
|
||||
getpwent(void);
|
||||
|
||||
/* Rewind the password stream to its first entry. */
|
||||
void
|
||||
setpwent(void);
|
||||
|
||||
/* Close the password stream; a later getpwent reopens from the start. */
|
||||
void
|
||||
endpwent(void);
|
||||
|
||||
int
|
||||
getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
int
|
||||
getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
int
|
||||
getpwent_r(struct passwd *pw, char *buf, size_t bufsize, struct passwd **result);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_PWD_H */
|
||||
@@ -0,0 +1,117 @@
|
||||
#ifndef VLIBC_SETJMP_H
|
||||
#define VLIBC_SETJMP_H
|
||||
|
||||
/*
|
||||
* vlibc — <setjmp.h>.
|
||||
*
|
||||
* Non-local jumps. setjmp(env) saves the calling environment and returns 0;
|
||||
* longjmp(env, val) makes that setjmp return val, with 0 coerced to 1 so the
|
||||
* two returns are distinguishable. sigsetjmp/siglongjmp additionally save and
|
||||
* restore the signal mask when the buffer's saved-mask flag is set.
|
||||
*
|
||||
* jmp_buf is an array type (C23 7.13.1 requires it); both types below are
|
||||
* arrays of unsigned long. The word layout is ABI: the x86_64 assembly in
|
||||
* src/setjmp/x86_64/{setjmp,longjmp,sigsetjmp,siglongjmp}.s reads and writes
|
||||
* exactly these slots. This comment is the single authoritative map — keep it
|
||||
* and those files in sync:
|
||||
*
|
||||
* [0] rbx callee-saved general registers (System V AMD64 ABI)
|
||||
* [1] rbp
|
||||
* [2] r12
|
||||
* [3] r13
|
||||
* [4] r14
|
||||
* [5] r15
|
||||
* [6] rsp the caller's stack pointer (just past the return address)
|
||||
* [7] rip the return address, where longjmp resumes execution
|
||||
* [8] mxcsr SSE control/status register (stmxcsr/ldmxcsr)
|
||||
* [9] x87 cw x87 control word (fnstcw/fldcw)
|
||||
*
|
||||
* sigjmp_buf extends the base layout with two more words:
|
||||
*
|
||||
* [10] saved-mask flag (1 when the signal mask was saved)
|
||||
* [11] signal mask (the x86_64 Linux sigset_t: a single 64-bit word)
|
||||
*
|
||||
* Because the base layout occupies words 0..9 of both types, the plain
|
||||
* setjmp/longjmp assembly works verbatim on sigjmp_buf storage as well.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Word counts and per-slot indices (see the authoritative map above). */
|
||||
#define VLIBC_JMP_BUF_WORDS 10
|
||||
#define VLIBC_JMP_RBX 0
|
||||
#define VLIBC_JMP_RBP 1
|
||||
#define VLIBC_JMP_R12 2
|
||||
#define VLIBC_JMP_R13 3
|
||||
#define VLIBC_JMP_R14 4
|
||||
#define VLIBC_JMP_R15 5
|
||||
#define VLIBC_JMP_RSP 6
|
||||
#define VLIBC_JMP_RIP 7
|
||||
#define VLIBC_JMP_MXCSR 8
|
||||
#define VLIBC_JMP_X87CW 9
|
||||
|
||||
#define VLIBC_SIGJMP_BUF_WORDS 12
|
||||
#define VLIBC_SIGJMP_FLAG 10
|
||||
#define VLIBC_SIGJMP_MASK 11
|
||||
|
||||
typedef unsigned long jmp_buf[VLIBC_JMP_BUF_WORDS];
|
||||
typedef unsigned long sigjmp_buf[VLIBC_SIGJMP_BUF_WORDS];
|
||||
|
||||
/*
|
||||
* Save the calling environment and return 0; after longjmp(env, val) the
|
||||
* matching setjmp invocation returns val. A macro (C23 7.13.1: usable without
|
||||
* a prototype in scope); it expands to _setjmp(env).
|
||||
*/
|
||||
#define setjmp(env) _setjmp(env)
|
||||
|
||||
/*
|
||||
* Like setjmp, plus the current signal mask is saved into env when savemask is
|
||||
* nonzero (siglongjmp then restores it). Macro expanding to
|
||||
* __sigsetjmp(env, savemask).
|
||||
*/
|
||||
#define sigsetjmp(env, savemask) __sigsetjmp(env, savemask)
|
||||
|
||||
/*
|
||||
* The save behind the setjmp macro; identical semantics (POSIX _setjmp).
|
||||
*/
|
||||
int
|
||||
_setjmp(jmp_buf env); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* Restore the environment saved by _setjmp/setjmp and make it return val, with
|
||||
* 0 coerced to 1. Does not return.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
_longjmp(jmp_buf env, int val); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* ISO C longjmp: same as _longjmp.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
longjmp(jmp_buf env, int val);
|
||||
|
||||
/*
|
||||
* The save behind the sigsetjmp macro. Implementation-reserved name, so the
|
||||
* public API keeps only the macro; declared because the macro expansion must
|
||||
* have a prototype in scope (C23 removed implicit declarations).
|
||||
*/
|
||||
int
|
||||
__sigsetjmp(sigjmp_buf env, int savemask); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* Restore the environment saved by sigsetjmp and make it return val, with 0
|
||||
* coerced to 1; when the buffer's saved-mask flag is set, the signal mask is
|
||||
* restored first. Does not return.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
siglongjmp(sigjmp_buf env, int val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SETJMP_H */
|
||||
@@ -0,0 +1,436 @@
|
||||
#ifndef VLIBC_SIGNAL_H
|
||||
#define VLIBC_SIGNAL_H
|
||||
|
||||
/*
|
||||
* vlibc — <signal.h>.
|
||||
*
|
||||
* Signal handling (POSIX.1-2008 base) plus the XSI/obsolete conveniences.
|
||||
* The core calls ride the kernel's rt_* signal ABI (SYS_rt_sigaction,
|
||||
* SYS_rt_sigprocmask, SYS_rt_sigpending, SYS_rt_sigsuspend,
|
||||
* SYS_rt_sigtimedwait, SYS_rt_sigqueueinfo) with the x86_64 kernel
|
||||
* sigset size of one 64-bit word.
|
||||
*
|
||||
* Level 1 (onlyposix): signal numbers, SIG_DFL/SIG_IGN/SIG_ERR, the
|
||||
* sigset_t manipulation and query calls, sigaction,
|
||||
* sigprocmask, sigpending, sigsuspend, sigwait,
|
||||
* sigwaitinfo, sigtimedwait, kill, killpg, raise,
|
||||
* pause, alarm, abort, sigqueue, and the signal()
|
||||
* convenience.
|
||||
* Level 2 (muslmimic): sigaltstack (and stack_t / SS_* / SIGSTKSZ),
|
||||
* ualarm, siginterrupt, psignal, psiginfo (XSI /
|
||||
* obsolete — sigaction/sigprocmask/alarm are POSIX
|
||||
* base and stay at level 1).
|
||||
*
|
||||
* The layout facts below are x86_64 kernel-ABI:
|
||||
*
|
||||
* - The kernel sigset_t is 8 bytes (one word); signal N occupies bit
|
||||
* N-1, signals 1..64 are usable (32..64 are the realtime signals).
|
||||
* The same single-word sigset_t is declared by <sys/select.h> and
|
||||
* <poll.h> under the shared VLIBC_SIGSET_T_DEFINED guard so pselect/
|
||||
* ppoll and the sig* API agree on the type no matter the include order.
|
||||
* - SIG_DFL/SIG_IGN/SIG_ERR are the magic handler values 0/1/-1 cast to
|
||||
* the handler type; the kernel dispatches on the raw value.
|
||||
* - The PUBLIC struct sigaction is the POSIX layout (handler, mask,
|
||||
* int flags, restorer). The kernel's rt_sigaction ABI layout is
|
||||
* DIFFERENT (handler, flags, restorer, mask — flags is a full word);
|
||||
* src/signal/sigaction.c converts between the two.
|
||||
* - SA_RESTORER (0x04000000) is the kernel-private bit that makes the
|
||||
* kernel jump to the user-supplied sa_restorer trampoline when the
|
||||
* handler returns; every real (non-DFL/IGN) handler install sets it
|
||||
* (see sigaction.c). It is masked out of the flags reported back.
|
||||
*
|
||||
* siginfo_t is the minimal 128-byte layout shared with <sys/wait.h>
|
||||
* under the VLIBC_INTERNAL_SIGINFO_DEFINED guard (see the block below);
|
||||
* either header may be included first. No SIGEV_* / SI_* constants are
|
||||
* defined: the signal-handling details behind them are owned by a later
|
||||
* todo and nothing here needs the names.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* struct timespec is defined by <time.h>; a forward declaration is enough
|
||||
* for sigtimedwait()'s pointer parameter, and the two headers may be
|
||||
* included in either order.
|
||||
*/
|
||||
struct timespec;
|
||||
|
||||
/* Signal numbers (x86_64 asm-generic values; kernel-ABI facts). */
|
||||
#define SIGHUP 1 /* hangup detected on controlling terminal */
|
||||
#define SIGINT 2 /* interactive attention (^C) */
|
||||
#define SIGQUIT 3 /* interactive quit (^\) */
|
||||
#define SIGILL 4 /* illegal instruction */
|
||||
#define SIGTRAP 5 /* trace/breakpoint trap */
|
||||
#define SIGABRT 6 /* abnormal termination (abort()) */
|
||||
#define SIGBUS 7 /* bus error */
|
||||
#define SIGFPE 8 /* floating-point exception */
|
||||
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
|
||||
#define SIGUSR1 10 /* user-defined signal 1 */
|
||||
#define SIGSEGV 11 /* invalid memory reference */
|
||||
#define SIGUSR2 12 /* user-defined signal 2 */
|
||||
#define SIGPIPE 13 /* write on a pipe with no reader */
|
||||
#define SIGALRM 14 /* real-time timer expired (alarm/ualarm/setitimer) */
|
||||
#define SIGTERM 15 /* termination request */
|
||||
#define SIGSTKFLT 16 /* stack fault (unused) */
|
||||
#define SIGCHLD 17 /* child stopped or terminated */
|
||||
#define SIGCONT 18 /* continue if stopped */
|
||||
#define SIGSTOP 19 /* stop (cannot be caught or ignored) */
|
||||
#define SIGTSTP 20 /* interactive stop (^Z) */
|
||||
#define SIGTTIN 21 /* background process read from terminal */
|
||||
#define SIGTTOU 22 /* background process write to terminal */
|
||||
#define SIGURG 23 /* urgent condition on a socket */
|
||||
#define SIGXCPU 24 /* CPU time limit exceeded */
|
||||
#define SIGXFSZ 25 /* file size limit exceeded */
|
||||
#define SIGVTALRM 26 /* virtual timer expired */
|
||||
#define SIGPROF 27 /* profiling timer expired */
|
||||
#define SIGWINCH 28 /* window size change */
|
||||
#define SIGIO 29 /* I/O now possible */
|
||||
#define SIGPWR 30 /* power failure */
|
||||
#define SIGSYS 31 /* bad system call */
|
||||
|
||||
/* Realtime signals: the 32..64 range, usable for user-defined purposes. */
|
||||
#define SIGRTMIN 32
|
||||
#define SIGRTMAX 64
|
||||
|
||||
/* One more than the highest signal number; signals 1..64 are usable. */
|
||||
#define NSIG 65
|
||||
|
||||
/* The magic handler values. The kernel dispatches on the raw value. */
|
||||
#define SIG_DFL ((void (*)(int))0) /* default action */
|
||||
#define SIG_IGN ((void (*)(int))1) /* ignore the signal */
|
||||
#define SIG_ERR ((void (*)(int)) - 1) /* signal() error return */
|
||||
|
||||
/*
|
||||
* Integer type that can be accessed as an atomic entity even when an
|
||||
* asynchronous signal interrupts the access (volatile sig_atomic_t is the
|
||||
* conventional spelling). int is atomic on x86_64.
|
||||
*/
|
||||
typedef int sig_atomic_t;
|
||||
|
||||
/* sigprocmask()/pthread_sigmask() how values (kernel-identical). */
|
||||
#define SIG_BLOCK 0 /* add the given set to the blocked mask */
|
||||
#define SIG_UNBLOCK 1 /* remove the given set from the blocked mask */
|
||||
#define SIG_SETMASK 2 /* replace the blocked mask with the given set */
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t,
|
||||
* where signal N is bit N-1 (see setjmp.h, whose sigsetjmp stores this
|
||||
* same word). The typedef is shared with <sys/select.h> and <poll.h>
|
||||
* under this guard, so pselect()/ppoll() and the sig* API agree on the
|
||||
* type regardless of include order.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Value carried with a queued (realtime) signal. sival_int and sival_ptr
|
||||
* are alternative views of the same 64-bit payload.
|
||||
*/
|
||||
union sigval
|
||||
{
|
||||
int sival_int; /* integer payload */
|
||||
void *sival_ptr; /* pointer payload */
|
||||
};
|
||||
|
||||
/* POSIX spelling of the payload type. */
|
||||
typedef union sigval sigval_t;
|
||||
|
||||
/*
|
||||
* Description of a delivered signal: number, errno, code and one of the
|
||||
* per-code unions. This is the minimal x86_64 kernel layout (128 bytes)
|
||||
* shared with <sys/wait.h>, which fills si_pid/si_uid/si_status/si_utime/
|
||||
* si_stime for waitid(); the two headers define this exact block under the
|
||||
* common VLIBC_INTERNAL_SIGINFO_DEFINED guard, so whichever is included
|
||||
* first wins and the other skips — the type is identical either way. The
|
||||
* kernel copies the full 128 bytes on signal delivery (sigaction with
|
||||
* SA_SIGINFO, sigwaitinfo, sigtimedwait), so the size must stay 128 and
|
||||
* the pad member guarantees it; the fields behind each si_code value
|
||||
* (si_addr, si_value, timers, ...) are owned by a later todo.
|
||||
*/
|
||||
#ifndef VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
#define VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
typedef struct
|
||||
{
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
int si_status; /* 24 */
|
||||
long si_utime; /* 32 — 8-byte clock_t, matches the kernel/glibc ABI */
|
||||
long si_stime; /* 40 */
|
||||
};
|
||||
int vlibc_siginfo_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
} siginfo_t;
|
||||
|
||||
_Static_assert(sizeof(siginfo_t) == 128, "siginfo_t must match the kernel size");
|
||||
_Static_assert(offsetof(siginfo_t, si_pid) == 16, "si_pid must sit at offset 16");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Action taken when signal sig is delivered. sa_handler and sa_sigaction
|
||||
* are two views of the same slot (POSIX: sa_sigaction is used when
|
||||
* SA_SIGINFO is set and receives (sig, siginfo_t *, void *) instead of
|
||||
* just sig). The layout — handler, mask, flags, restorer — is the PUBLIC
|
||||
* POSIX order; the kernel ABI uses a different order (see the file-top
|
||||
* note) that sigaction() translates.
|
||||
*/
|
||||
struct sigaction
|
||||
{
|
||||
union
|
||||
{
|
||||
void (*sa_handler)(int); /* SIG_DFL/SIG_IGN/a handler */
|
||||
void (*sa_sigaction)(int, siginfo_t *, void *); /* SA_SIGINFO form */
|
||||
};
|
||||
sigset_t sa_mask; /* signals additionally blocked in the handler */
|
||||
int sa_flags; /* SA_* bits below */
|
||||
void (*sa_restorer)(void); /* kernel-private; do not set (see top) */
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct sigaction) == 32, "struct sigaction must be 32 bytes");
|
||||
_Static_assert(offsetof(struct sigaction, sa_mask) == 8, "sa_mask must sit at offset 8");
|
||||
_Static_assert(offsetof(struct sigaction, sa_flags) == 16, "sa_flags must sit at offset 16");
|
||||
_Static_assert(offsetof(struct sigaction, sa_restorer) == 24, "sa_restorer must sit at offset 24");
|
||||
|
||||
/* sa_flags bits (x86_64 kernel values; SA_RESTORER is kernel-private). */
|
||||
#define SA_NOCLDSTOP 1 /* do not generate SIGCHLD when children stop */
|
||||
#define SA_NOCLDWAIT 2 /* do not leave zombies on child exit */
|
||||
#define SA_SIGINFO 4 /* call the handler with the siginfo form */
|
||||
#define SA_RESTORER 0x04000000 /* handler returns via sa_restorer (kernel) */
|
||||
#define SA_ONSTACK 0x08000000 /* run the handler on the alternate stack */
|
||||
#define SA_RESTART 0x10000000 /* restart interrupted syscalls after the handler */
|
||||
#define SA_NODEFER 0x40000000 /* do not block the delivered signal in its handler */
|
||||
#define SA_RESETHAND 0x80000000 /* reset the disposition to SIG_DFL on entry */
|
||||
|
||||
/*
|
||||
* Install the action act for signal sig (NULL leaves it unchanged) and
|
||||
* store the previous action through oact (NULL skips the store). Return
|
||||
* 0, or -1 with errno set. sig must be in 1..64 and (for a catching
|
||||
* action) not SIGKILL/SIGSTOP; the kernel rejects invalid values with
|
||||
* EINVAL. Real handler pointers are installed with SA_RESTORER and the
|
||||
* internal return trampoline so the handler may return normally.
|
||||
*/
|
||||
int
|
||||
sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
|
||||
|
||||
/*
|
||||
* Inspect or change the calling thread's blocked-signal mask. how is
|
||||
* SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK; set is the mask operand (NULL
|
||||
* queries without changing: how is then ignored) and the previous mask is
|
||||
* stored through oldset (NULL skips the store). Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
|
||||
|
||||
/* Store the set of blocked-and-pending signals through set. 0 or -1. */
|
||||
int
|
||||
sigpending(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Atomically install mask as the blocked mask and wait until a signal
|
||||
* whose delivery is not blocked by mask is caught; when its handler
|
||||
* returns, the previous mask is restored and sigsuspend returns -1 with
|
||||
* errno EINTR.
|
||||
*/
|
||||
int
|
||||
sigsuspend(const sigset_t *mask);
|
||||
|
||||
/* Empty set: no signal blocked, and no member set. Always 0. */
|
||||
int
|
||||
sigemptyset(sigset_t *set);
|
||||
|
||||
/* Set every signal 1..64 blocked/member. Always 0. */
|
||||
int
|
||||
sigfillset(sigset_t *set);
|
||||
|
||||
/*
|
||||
* Add/remove signal sig to/from set. Return 0, or -1 with errno EINVAL
|
||||
* when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigaddset(sigset_t *set, int sig);
|
||||
int
|
||||
sigdelset(sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Nonzero when sig is a member of set, 0 otherwise, or -1 with errno
|
||||
* EINVAL when sig is outside 1..64.
|
||||
*/
|
||||
int
|
||||
sigismember(const sigset_t *set, int sig);
|
||||
|
||||
/*
|
||||
* Synchronously wait for one of the signals in set (which should be
|
||||
* blocked in the calling thread) and store the delivered signal number
|
||||
* through sig. Return 0 on success, or the error number (EINTR is
|
||||
* retried internally). The signal is consumed and never delivered to a
|
||||
* handler.
|
||||
*/
|
||||
int
|
||||
sigwait(const sigset_t *restrict set, int *restrict sig);
|
||||
|
||||
/*
|
||||
* Like sigwait, but return the delivered signal number directly (or -1
|
||||
* with errno set) and, when info is not NULL, store the full 128-byte
|
||||
* kernel siginfo through it.
|
||||
*/
|
||||
int
|
||||
sigwaitinfo(const sigset_t *restrict set, siginfo_t *restrict info);
|
||||
|
||||
/*
|
||||
* sigwaitinfo bounded by timeout (relative; NULL waits indefinitely).
|
||||
* Returns the signal number, 0 is never returned for a successful wait,
|
||||
* or -1 with errno set (EAGAIN on timeout, EINTR if a handler ran).
|
||||
*/
|
||||
int
|
||||
sigtimedwait(const sigset_t *restrict set, siginfo_t *restrict info,
|
||||
const struct timespec *restrict timeout);
|
||||
|
||||
/*
|
||||
* Send signal sig to the process pid (negative pid targets a process
|
||||
* group; 0 targets the caller's process group; see killpg). Return 0, or
|
||||
* -1 with errno set. Permission, existence and signal validity are
|
||||
* checked by the kernel.
|
||||
*/
|
||||
int
|
||||
kill(pid_t pid, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to every process in the process group pgrp (0 selects
|
||||
* the caller's process group). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
killpg(pid_t pgrp, int sig);
|
||||
|
||||
/*
|
||||
* Send signal sig to the calling thread (thread-directed, so it is
|
||||
* delivered even when another thread of the process has it blocked).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
raise(int sig);
|
||||
|
||||
/*
|
||||
* Wait until a signal is caught, then return -1 with errno EINTR. If the
|
||||
* process is terminated by the signal instead, pause never returns.
|
||||
*/
|
||||
int
|
||||
pause(void);
|
||||
|
||||
/*
|
||||
* Schedule delivery of SIGALRM to the calling process after seconds
|
||||
* seconds (0 cancels any pending alarm). Return the number of seconds
|
||||
* remaining on any previously scheduled alarm, or 0.
|
||||
*/
|
||||
unsigned
|
||||
alarm(unsigned seconds);
|
||||
|
||||
/*
|
||||
* Abnormally terminate the calling process: raise SIGABRT with default
|
||||
* disposition — if SIGABRT is blocked it is unblocked first, and if it
|
||||
* is caught or ignored the disposition is reset to SIG_DFL and the raise
|
||||
* repeated. If that still returns, terminate with exit status 134 as a
|
||||
* last resort. abort never returns.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
abort(void);
|
||||
|
||||
/*
|
||||
* Send signal sig with payload value to process pid, as if by a kernel
|
||||
* queue operation (the kernel sees a negative si_code, SI_QUEUE). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigqueue(pid_t pid, int sig, const union sigval value);
|
||||
|
||||
/*
|
||||
* Install handler as the action for sig with SA_RESTART (BSD semantics:
|
||||
* syscalls interrupted by the signal are restarted). Return the previous
|
||||
* handler, or SIG_ERR with errno set.
|
||||
*/
|
||||
void (*signal(int sig, void (*handler)(int)))(int);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and obsolete conveniences. */
|
||||
|
||||
/*
|
||||
* Alternate signal stack descriptor. ss_sp/ss_size name a region the
|
||||
* kernel switches to when delivering a handler installed with SA_ONSTACK
|
||||
* (see sigaltstack below). stack_t is the POSIX spelling.
|
||||
*/
|
||||
typedef struct sigaltstack
|
||||
{
|
||||
void *ss_sp; /* stack base or current stack base */
|
||||
int ss_flags; /* SS_ONSTACK/SS_DISABLE */
|
||||
size_t ss_size; /* stack bytes */
|
||||
} stack_t;
|
||||
|
||||
/* ss_flags values. */
|
||||
#define SS_ONSTACK 1 /* the process is currently executing on this stack */
|
||||
#define SS_DISABLE 2 /* the alternate stack is currently disabled */
|
||||
|
||||
/* Minimum / default alternate stack sizes (x86_64 values). */
|
||||
#define MINSIGSTKSZ 2048
|
||||
#define SIGSTKSZ 8192
|
||||
|
||||
/*
|
||||
* Install ss as the alternate signal stack (NULL leaves it unchanged)
|
||||
* and store the previous descriptor through oss (NULL skips the store).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
sigaltstack(const stack_t *restrict ss, stack_t *restrict oss);
|
||||
|
||||
/*
|
||||
* Schedule SIGALRM after usecs microseconds, repeating every interval
|
||||
* microseconds (0 fires once). Return the number of microseconds
|
||||
* remaining on any previously scheduled alarm, or -1 on error.
|
||||
*/
|
||||
useconds_t
|
||||
ualarm(useconds_t usecs, useconds_t interval);
|
||||
|
||||
/*
|
||||
* Toggle the SA_RESTART bit of sig's disposition: flag nonzero removes
|
||||
* it (interrupted syscalls return EINTR, System V semantics), flag zero
|
||||
* restores it. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
siginterrupt(int sig, int flag);
|
||||
|
||||
/*
|
||||
* Write s (when non-NULL and nonempty), ": " and strsignal(sig) to
|
||||
* stderr. The output is a diagnostic only.
|
||||
*/
|
||||
void
|
||||
psignal(int sig, const char *s);
|
||||
|
||||
/*
|
||||
* Like psignal, but the signal number and details are taken from the
|
||||
* siginfo si (si_signo names the signal). si may be NULL, in which case
|
||||
* only the message prefix is written.
|
||||
*/
|
||||
void
|
||||
psiginfo(const siginfo_t *si, const char *s);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SIGNAL_H */
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef VLIBC_STDALIGN_H
|
||||
#define VLIBC_STDALIGN_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdalign.h>.
|
||||
*
|
||||
* In C23 alignas and alignof are keywords, so this header defines only the
|
||||
* feature-test macros __alignas_is_defined and __alignof_is_defined. In C17
|
||||
* (and older) it additionally maps them onto the _Alignas/_Alignof keywords.
|
||||
* In C++ the two names are keywords as well.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifndef __cplusplus
|
||||
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L
|
||||
#define alignas _Alignas
|
||||
#define alignof _Alignof
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define __alignas_is_defined 1
|
||||
#define __alignof_is_defined 1
|
||||
|
||||
#endif /* VLIBC_STDALIGN_H */
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef VLIBC_STDARG_H
|
||||
#define VLIBC_STDARG_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdarg.h>.
|
||||
*
|
||||
* Variable argument lists. The entire header defers to the compiler's
|
||||
* builtin support: __builtin_va_list names the ABI's va_list layout and the
|
||||
* __builtin_va_* entry points manipulate it. Nothing here depends on a
|
||||
* system header.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(ap, last) __builtin_va_start((ap), (last))
|
||||
#define va_end(ap) __builtin_va_end((ap))
|
||||
#define va_arg(ap, type) __builtin_va_arg((ap), type)
|
||||
#define va_copy(dst, src) __builtin_va_copy((dst), (src))
|
||||
|
||||
#endif /* VLIBC_STDARG_H */
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef VLIBC_STDATOMIC_H
|
||||
#define VLIBC_STDATOMIC_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdatomic.h>.
|
||||
*
|
||||
* Atomics (C23). All operations defer to the GCC __atomic_* builtins, the
|
||||
* same idiom src/internal/atomic.h uses: on x86_64 every standard atomic
|
||||
* type here is lock-free in hardware, so the builtins inline to plain
|
||||
* instructions and never call into libatomic. The header is fully
|
||||
* self-contained (no system stdatomic.h) and compiles as C17-style code too,
|
||||
* since _Atomic remains a keyword.
|
||||
*
|
||||
* The atomic_* operations are function-like macros over the polymorphic
|
||||
* builtins (C23 allows any atomic operation to be implemented as a macro),
|
||||
* which keeps them type-generic across every atomic_* type without a
|
||||
* _Generic dispatch table.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Memory ordering. The enum values are the GCC __ATOMIC_* constants. */
|
||||
typedef enum
|
||||
{
|
||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
||||
memory_order_consume = __ATOMIC_CONSUME,
|
||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
||||
memory_order_release = __ATOMIC_RELEASE,
|
||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
||||
memory_order_seq_cst = __ATOMIC_SEQ_CST
|
||||
} memory_order;
|
||||
|
||||
/* Lock-free guarantees for each type (0: never, 1: sometimes, 2: always). */
|
||||
#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE
|
||||
#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
|
||||
#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE
|
||||
#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE
|
||||
#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE
|
||||
#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
|
||||
#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
|
||||
#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
|
||||
#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
|
||||
#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
|
||||
|
||||
/* Atomic scalar types. */
|
||||
typedef _Atomic _Bool atomic_bool;
|
||||
typedef _Atomic char atomic_char;
|
||||
typedef _Atomic signed char atomic_schar;
|
||||
typedef _Atomic unsigned char atomic_uchar;
|
||||
typedef _Atomic short atomic_short;
|
||||
typedef _Atomic unsigned short atomic_ushort;
|
||||
typedef _Atomic int atomic_int;
|
||||
typedef _Atomic unsigned int atomic_uint;
|
||||
typedef _Atomic long atomic_long;
|
||||
typedef _Atomic unsigned long atomic_ulong;
|
||||
typedef _Atomic long long atomic_llong;
|
||||
typedef _Atomic unsigned long long atomic_ullong;
|
||||
typedef _Atomic __CHAR16_TYPE__ atomic_char16_t;
|
||||
typedef _Atomic __CHAR32_TYPE__ atomic_char32_t;
|
||||
typedef _Atomic __WCHAR_TYPE__ atomic_wchar_t;
|
||||
typedef _Atomic int_least8_t atomic_int_least8_t;
|
||||
typedef _Atomic uint_least8_t atomic_uint_least8_t;
|
||||
typedef _Atomic int_least16_t atomic_int_least16_t;
|
||||
typedef _Atomic uint_least16_t atomic_uint_least16_t;
|
||||
typedef _Atomic int_least32_t atomic_int_least32_t;
|
||||
typedef _Atomic uint_least32_t atomic_uint_least32_t;
|
||||
typedef _Atomic int_least64_t atomic_int_least64_t;
|
||||
typedef _Atomic uint_least64_t atomic_uint_least64_t;
|
||||
typedef _Atomic int_fast8_t atomic_int_fast8_t;
|
||||
typedef _Atomic uint_fast8_t atomic_uint_fast8_t;
|
||||
typedef _Atomic int_fast16_t atomic_int_fast16_t;
|
||||
typedef _Atomic uint_fast16_t atomic_uint_fast16_t;
|
||||
typedef _Atomic int_fast32_t atomic_int_fast32_t;
|
||||
typedef _Atomic uint_fast32_t atomic_uint_fast32_t;
|
||||
typedef _Atomic int_fast64_t atomic_int_fast64_t;
|
||||
typedef _Atomic uint_fast64_t atomic_uint_fast64_t;
|
||||
typedef _Atomic intptr_t atomic_intptr_t;
|
||||
typedef _Atomic uintptr_t atomic_uintptr_t;
|
||||
typedef _Atomic size_t atomic_size_t;
|
||||
typedef _Atomic ptrdiff_t atomic_ptrdiff_t;
|
||||
typedef _Atomic intmax_t atomic_intmax_t;
|
||||
typedef _Atomic uintmax_t atomic_uintmax_t;
|
||||
|
||||
/* The only type the standard guarantees to be lock-free. */
|
||||
typedef _Atomic unsigned int atomic_flag;
|
||||
|
||||
/* Initializer for an atomic_flag object. */
|
||||
#define ATOMIC_FLAG_INIT 0
|
||||
|
||||
/*
|
||||
* The operations below are macros because the __atomic_* builtins are
|
||||
* polymorphic: they accept any 1/2/4/8-byte scalar or pointer type, so a
|
||||
* single macro serves every atomic_* type. The (object) argument is the
|
||||
* address of the atomic object, matching the standard's function signatures.
|
||||
*/
|
||||
|
||||
/* Initialize the atomic object to value (a relaxed store). */
|
||||
#define atomic_init(object, value) __atomic_store_n((object), (value), __ATOMIC_RELAXED)
|
||||
|
||||
/* Load the current value. */
|
||||
#define atomic_load(object) __atomic_load_n((object), __ATOMIC_SEQ_CST)
|
||||
#define atomic_load_explicit(object, order) __atomic_load_n((object), (order))
|
||||
|
||||
/* Store value. */
|
||||
#define atomic_store(object, value) __atomic_store_n((object), (value), __ATOMIC_SEQ_CST)
|
||||
#define atomic_store_explicit(object, value, order) __atomic_store_n((object), (value), (order))
|
||||
|
||||
/* Exchange object's value with desired; returns the previous value. */
|
||||
#define atomic_exchange(object, desired) __atomic_exchange_n((object), (desired), __ATOMIC_SEQ_CST)
|
||||
#define atomic_exchange_explicit(object, desired, order) \
|
||||
__atomic_exchange_n((object), (desired), (order))
|
||||
|
||||
/*
|
||||
* Compare-and-swap: store desired into object iff *object equals *expected.
|
||||
* On failure *expected is updated with the actual value and the macro
|
||||
* returns false. The first memorder governs the read-modify-write on
|
||||
* success, the second the load on failure.
|
||||
*/
|
||||
#define atomic_compare_exchange_strong(object, expected, desired) \
|
||||
__atomic_compare_exchange_n((object), (expected), (desired), 0, __ATOMIC_SEQ_CST, \
|
||||
__ATOMIC_SEQ_CST)
|
||||
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure) \
|
||||
__atomic_compare_exchange_n((object), (expected), (desired), 0, (success), (failure))
|
||||
#define atomic_compare_exchange_weak(object, expected, desired) \
|
||||
__atomic_compare_exchange_n((object), (expected), (desired), 1, __ATOMIC_SEQ_CST, \
|
||||
__ATOMIC_SEQ_CST)
|
||||
#define atomic_compare_exchange_weak_explicit(object, expected, desired, success, failure) \
|
||||
__atomic_compare_exchange_n((object), (expected), (desired), 1, (success), (failure))
|
||||
|
||||
/* Fetch and add; returns the previous value. */
|
||||
#define atomic_fetch_add(object, operand) __atomic_fetch_add((object), (operand), __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_add_explicit(object, operand, order) \
|
||||
__atomic_fetch_add((object), (operand), (order))
|
||||
|
||||
/* Fetch and subtract; returns the previous value. */
|
||||
#define atomic_fetch_sub(object, operand) __atomic_fetch_sub((object), (operand), __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_sub_explicit(object, operand, order) \
|
||||
__atomic_fetch_sub((object), (operand), (order))
|
||||
|
||||
/* Fetch and bitwise-or; returns the previous value. */
|
||||
#define atomic_fetch_or(object, operand) __atomic_fetch_or((object), (operand), __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_or_explicit(object, operand, order) \
|
||||
__atomic_fetch_or((object), (operand), (order))
|
||||
|
||||
/* Fetch and bitwise-xor; returns the previous value. */
|
||||
#define atomic_fetch_xor(object, operand) __atomic_fetch_xor((object), (operand), __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_xor_explicit(object, operand, order) \
|
||||
__atomic_fetch_xor((object), (operand), (order))
|
||||
|
||||
/* Fetch and bitwise-and; returns the previous value. */
|
||||
#define atomic_fetch_and(object, operand) __atomic_fetch_and((object), (operand), __ATOMIC_SEQ_CST)
|
||||
#define atomic_fetch_and_explicit(object, operand, order) \
|
||||
__atomic_fetch_and((object), (operand), (order))
|
||||
|
||||
/* atomic_flag: test-and-set (returns the previous flag value). */
|
||||
#define atomic_flag_test_and_set(object) __atomic_test_and_set((object), __ATOMIC_SEQ_CST)
|
||||
#define atomic_flag_test_and_set_explicit(object, order) __atomic_test_and_set((object), (order))
|
||||
|
||||
/* atomic_flag: clear. */
|
||||
#define atomic_flag_clear(object) __atomic_clear((object), __ATOMIC_SEQ_CST)
|
||||
#define atomic_flag_clear_explicit(object, order) __atomic_clear((object), (order))
|
||||
|
||||
/* Non-zero iff *object is always lock-free. */
|
||||
#define atomic_is_lock_free(object) __atomic_is_lock_free(sizeof *(object), (object))
|
||||
|
||||
/* Fences. */
|
||||
#define atomic_thread_fence(order) __atomic_thread_fence((order))
|
||||
#define atomic_signal_fence(order) __atomic_signal_fence((order))
|
||||
|
||||
/* Carry a dependency through a load without a synchronization edge. */
|
||||
#define kill_dependency(y) (y)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_STDATOMIC_H */
|
||||
@@ -0,0 +1,496 @@
|
||||
#ifndef VLIBC_STDBIT_H
|
||||
#define VLIBC_STDBIT_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdbit.h>.
|
||||
*
|
||||
* Bit and byte manipulation (C23 7.18). Every per-width operation is a
|
||||
* static inline function over the GCC counting builtins
|
||||
* (__builtin_clz/clzl/clzll, __builtin_ctz/ctzl/ctzll,
|
||||
* __builtin_popcount/popcountl/popcountll), so no call escapes into the
|
||||
* library. The type-generic names (stdc_leading_zeros, ...) dispatch through
|
||||
* _Generic to the per-width functions; signed arguments convert to the
|
||||
* unsigned type of the same rank, which preserves the bit pattern exactly as
|
||||
* C23 specifies. Widths match x86_64: int is 32-bit, long is 64-bit.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* stdc_leading_zeros: number of 0 bits before the first 1 bit, or the width. */
|
||||
static inline unsigned int
|
||||
stdc_leading_zeros_uc(unsigned char x)
|
||||
{
|
||||
return x == 0 ? 8u : (unsigned int)(__builtin_clz(x) - 24);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_zeros_us(unsigned short x)
|
||||
{
|
||||
return x == 0 ? 16u : (unsigned int)(__builtin_clz(x) - 16);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_zeros_ui(unsigned int x)
|
||||
{
|
||||
return x == 0 ? 32u : (unsigned int)__builtin_clz(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_zeros_ul(unsigned long x)
|
||||
{
|
||||
return x == 0 ? 64u : (unsigned int)__builtin_clzl(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_zeros_ull(unsigned long long x)
|
||||
{
|
||||
return x == 0 ? 64u : (unsigned int)__builtin_clzll(x);
|
||||
}
|
||||
|
||||
/* stdc_leading_ones: number of 1 bits before the first 0 bit, or the width. */
|
||||
static inline unsigned int
|
||||
stdc_leading_ones_uc(unsigned char x)
|
||||
{
|
||||
return x == UCHAR_MAX ? 8u : (unsigned int)(__builtin_clz((unsigned char)~x) - 24);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_ones_us(unsigned short x)
|
||||
{
|
||||
return x == USHRT_MAX ? 16u : (unsigned int)(__builtin_clz((unsigned short)~x) - 16);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_ones_ui(unsigned int x)
|
||||
{
|
||||
return x == UINT_MAX ? 32u : (unsigned int)__builtin_clz(~x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_ones_ul(unsigned long x)
|
||||
{
|
||||
return x == ULONG_MAX ? 64u : (unsigned int)__builtin_clzl(~x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_leading_ones_ull(unsigned long long x)
|
||||
{
|
||||
return x == ULLONG_MAX ? 64u : (unsigned int)__builtin_clzll(~x);
|
||||
}
|
||||
|
||||
/* stdc_trailing_zeros: number of 0 bits after the last 1 bit, or the width. */
|
||||
static inline unsigned int
|
||||
stdc_trailing_zeros_uc(unsigned char x)
|
||||
{
|
||||
return x == 0 ? 8u : (unsigned int)__builtin_ctz(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_zeros_us(unsigned short x)
|
||||
{
|
||||
return x == 0 ? 16u : (unsigned int)__builtin_ctz(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_zeros_ui(unsigned int x)
|
||||
{
|
||||
return x == 0 ? 32u : (unsigned int)__builtin_ctz(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_zeros_ul(unsigned long x)
|
||||
{
|
||||
return x == 0 ? 64u : (unsigned int)__builtin_ctzl(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_zeros_ull(unsigned long long x)
|
||||
{
|
||||
return x == 0 ? 64u : (unsigned int)__builtin_ctzll(x);
|
||||
}
|
||||
|
||||
/* stdc_trailing_ones: number of 1 bits after the last 0 bit, or the width. */
|
||||
static inline unsigned int
|
||||
stdc_trailing_ones_uc(unsigned char x)
|
||||
{
|
||||
return x == UCHAR_MAX ? 8u : (unsigned int)__builtin_ctz((unsigned char)~x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_ones_us(unsigned short x)
|
||||
{
|
||||
return x == USHRT_MAX ? 16u : (unsigned int)__builtin_ctz((unsigned short)~x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_ones_ui(unsigned int x)
|
||||
{
|
||||
return x == UINT_MAX ? 32u : (unsigned int)__builtin_ctz(~x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_ones_ul(unsigned long x)
|
||||
{
|
||||
return x == ULONG_MAX ? 64u : (unsigned int)__builtin_ctzl(~x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_trailing_ones_ull(unsigned long long x)
|
||||
{
|
||||
return x == ULLONG_MAX ? 64u : (unsigned int)__builtin_ctzll(~x);
|
||||
}
|
||||
|
||||
/*
|
||||
* stdc_first_*: index (from the most significant bit) of the first zero/one
|
||||
* bit, or the width when no such bit exists. By construction, the index of
|
||||
* the first zero from the top equals the count of leading ones, and so on.
|
||||
*/
|
||||
static inline unsigned int
|
||||
stdc_first_leading_zero_uc(unsigned char x)
|
||||
{
|
||||
return stdc_leading_ones_uc(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_zero_us(unsigned short x)
|
||||
{
|
||||
return stdc_leading_ones_us(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_zero_ui(unsigned int x)
|
||||
{
|
||||
return stdc_leading_ones_ui(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_zero_ul(unsigned long x)
|
||||
{
|
||||
return stdc_leading_ones_ul(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_zero_ull(unsigned long long x)
|
||||
{
|
||||
return stdc_leading_ones_ull(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_one_uc(unsigned char x)
|
||||
{
|
||||
return stdc_leading_zeros_uc(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_one_us(unsigned short x)
|
||||
{
|
||||
return stdc_leading_zeros_us(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_one_ui(unsigned int x)
|
||||
{
|
||||
return stdc_leading_zeros_ui(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_one_ul(unsigned long x)
|
||||
{
|
||||
return stdc_leading_zeros_ul(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_leading_one_ull(unsigned long long x)
|
||||
{
|
||||
return stdc_leading_zeros_ull(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_zero_uc(unsigned char x)
|
||||
{
|
||||
return stdc_trailing_ones_uc(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_zero_us(unsigned short x)
|
||||
{
|
||||
return stdc_trailing_ones_us(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_zero_ui(unsigned int x)
|
||||
{
|
||||
return stdc_trailing_ones_ui(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_zero_ul(unsigned long x)
|
||||
{
|
||||
return stdc_trailing_ones_ul(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_zero_ull(unsigned long long x)
|
||||
{
|
||||
return stdc_trailing_ones_ull(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_one_uc(unsigned char x)
|
||||
{
|
||||
return stdc_trailing_zeros_uc(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_one_us(unsigned short x)
|
||||
{
|
||||
return stdc_trailing_zeros_us(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_one_ui(unsigned int x)
|
||||
{
|
||||
return stdc_trailing_zeros_ui(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_one_ul(unsigned long x)
|
||||
{
|
||||
return stdc_trailing_zeros_ul(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_first_trailing_one_ull(unsigned long long x)
|
||||
{
|
||||
return stdc_trailing_zeros_ull(x);
|
||||
}
|
||||
|
||||
/* stdc_count_ones: number of 1 bits. */
|
||||
static inline unsigned int
|
||||
stdc_count_ones_uc(unsigned char x)
|
||||
{
|
||||
return (unsigned int)__builtin_popcount(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_ones_us(unsigned short x)
|
||||
{
|
||||
return (unsigned int)__builtin_popcount(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_ones_ui(unsigned int x)
|
||||
{
|
||||
return (unsigned int)__builtin_popcount(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_ones_ul(unsigned long x)
|
||||
{
|
||||
return (unsigned int)__builtin_popcountl(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_ones_ull(unsigned long long x)
|
||||
{
|
||||
return (unsigned int)__builtin_popcountll(x);
|
||||
}
|
||||
|
||||
/* stdc_count_zeros: number of 0 bits. */
|
||||
static inline unsigned int
|
||||
stdc_count_zeros_uc(unsigned char x)
|
||||
{
|
||||
return 8u - stdc_count_ones_uc(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_zeros_us(unsigned short x)
|
||||
{
|
||||
return 16u - stdc_count_ones_us(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_zeros_ui(unsigned int x)
|
||||
{
|
||||
return 32u - stdc_count_ones_ui(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_zeros_ul(unsigned long x)
|
||||
{
|
||||
return 64u - stdc_count_ones_ul(x);
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_count_zeros_ull(unsigned long long x)
|
||||
{
|
||||
return 64u - stdc_count_ones_ull(x);
|
||||
}
|
||||
|
||||
/* stdc_has_single_bit: true iff exactly one bit is set. */
|
||||
static inline _Bool
|
||||
stdc_has_single_bit_uc(unsigned char x)
|
||||
{
|
||||
return x != 0 && (x & (x - 1U)) == 0;
|
||||
}
|
||||
|
||||
static inline _Bool
|
||||
stdc_has_single_bit_us(unsigned short x)
|
||||
{
|
||||
return x != 0 && (x & (x - 1U)) == 0;
|
||||
}
|
||||
|
||||
static inline _Bool
|
||||
stdc_has_single_bit_ui(unsigned int x)
|
||||
{
|
||||
return x != 0 && (x & (x - 1U)) == 0;
|
||||
}
|
||||
|
||||
static inline _Bool
|
||||
stdc_has_single_bit_ul(unsigned long x)
|
||||
{
|
||||
return x != 0 && (x & (x - 1UL)) == 0;
|
||||
}
|
||||
|
||||
static inline _Bool
|
||||
stdc_has_single_bit_ull(unsigned long long x)
|
||||
{
|
||||
return x != 0 && (x & (x - 1ULL)) == 0;
|
||||
}
|
||||
|
||||
/* stdc_bit_width: bits needed to represent x (0 for 0, 1 for 1). */
|
||||
static inline unsigned int
|
||||
stdc_bit_width_uc(unsigned char x)
|
||||
{
|
||||
return x == 0 ? 0u : (unsigned int)(32 - __builtin_clz(x));
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_bit_width_us(unsigned short x)
|
||||
{
|
||||
return x == 0 ? 0u : (unsigned int)(32 - __builtin_clz(x));
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_bit_width_ui(unsigned int x)
|
||||
{
|
||||
return x == 0 ? 0u : (unsigned int)(32 - __builtin_clz(x));
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_bit_width_ul(unsigned long x)
|
||||
{
|
||||
return x == 0 ? 0u : (unsigned int)(64 - __builtin_clzl(x));
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_bit_width_ull(unsigned long long x)
|
||||
{
|
||||
return x == 0 ? 0u : (unsigned int)(64 - __builtin_clzll(x));
|
||||
}
|
||||
|
||||
/* stdc_bit_floor: largest power of 2 not greater than x (0 for 0). */
|
||||
static inline unsigned char
|
||||
stdc_bit_floor_uc(unsigned char x)
|
||||
{
|
||||
return (unsigned char)(x == 0 ? 0 : (1u << (32 - __builtin_clz(x) - 1)));
|
||||
}
|
||||
|
||||
static inline unsigned short
|
||||
stdc_bit_floor_us(unsigned short x)
|
||||
{
|
||||
return (unsigned short)(x == 0 ? 0 : (1u << (32 - __builtin_clz(x) - 1)));
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_bit_floor_ui(unsigned int x)
|
||||
{
|
||||
return x == 0 ? 0u : (1u << (32 - __builtin_clz(x) - 1));
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
stdc_bit_floor_ul(unsigned long x)
|
||||
{
|
||||
return x == 0 ? 0ul : (1ul << (64 - __builtin_clzl(x) - 1));
|
||||
}
|
||||
|
||||
static inline unsigned long long
|
||||
stdc_bit_floor_ull(unsigned long long x)
|
||||
{
|
||||
return x == 0 ? 0ull : (1ull << (64 - __builtin_clzll(x) - 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* stdc_bit_ceil: smallest power of 2 not less than x. The result is
|
||||
* undefined when it is not representable in the type of x, per C23.
|
||||
*/
|
||||
static inline unsigned char
|
||||
stdc_bit_ceil_uc(unsigned char x)
|
||||
{
|
||||
return (unsigned char)(x <= 1 ? 1 : (1u << (32 - __builtin_clz(x - 1))));
|
||||
}
|
||||
|
||||
static inline unsigned short
|
||||
stdc_bit_ceil_us(unsigned short x)
|
||||
{
|
||||
return (unsigned short)(x <= 1 ? 1 : (1u << (32 - __builtin_clz(x - 1))));
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
stdc_bit_ceil_ui(unsigned int x)
|
||||
{
|
||||
return x <= 1 ? 1u : (1u << (32 - __builtin_clz(x - 1)));
|
||||
}
|
||||
|
||||
static inline unsigned long
|
||||
stdc_bit_ceil_ul(unsigned long x)
|
||||
{
|
||||
return x <= 1 ? 1ul : (1ul << (64 - __builtin_clzl(x - 1)));
|
||||
}
|
||||
|
||||
static inline unsigned long long
|
||||
stdc_bit_ceil_ull(unsigned long long x)
|
||||
{
|
||||
return x <= 1 ? 1ull : (1ull << (64 - __builtin_clzll(x - 1)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Type-generic dispatch: map the (lvalue-converted) argument type to the
|
||||
* per-width function. Signed types route to the unsigned function of the
|
||||
* same rank, which preserves the bit pattern as C23 requires.
|
||||
*/
|
||||
#define VLIBC_STDBIT_DISPATCH(fn, x) \
|
||||
_Generic((x), \
|
||||
_Bool: fn##_uc, \
|
||||
char: fn##_uc, \
|
||||
signed char: fn##_uc, \
|
||||
unsigned char: fn##_uc, \
|
||||
short: fn##_us, \
|
||||
unsigned short: fn##_us, \
|
||||
int: fn##_ui, \
|
||||
unsigned int: fn##_ui, \
|
||||
long: fn##_ul, \
|
||||
unsigned long: fn##_ul, \
|
||||
long long: fn##_ull, \
|
||||
unsigned long long: fn##_ull)(x)
|
||||
|
||||
#define stdc_leading_zeros(x) VLIBC_STDBIT_DISPATCH(stdc_leading_zeros, x)
|
||||
#define stdc_leading_ones(x) VLIBC_STDBIT_DISPATCH(stdc_leading_ones, x)
|
||||
#define stdc_trailing_zeros(x) VLIBC_STDBIT_DISPATCH(stdc_trailing_zeros, x)
|
||||
#define stdc_trailing_ones(x) VLIBC_STDBIT_DISPATCH(stdc_trailing_ones, x)
|
||||
#define stdc_first_leading_zero(x) VLIBC_STDBIT_DISPATCH(stdc_first_leading_zero, x)
|
||||
#define stdc_first_leading_one(x) VLIBC_STDBIT_DISPATCH(stdc_first_leading_one, x)
|
||||
#define stdc_first_trailing_zero(x) VLIBC_STDBIT_DISPATCH(stdc_first_trailing_zero, x)
|
||||
#define stdc_first_trailing_one(x) VLIBC_STDBIT_DISPATCH(stdc_first_trailing_one, x)
|
||||
#define stdc_count_zeros(x) VLIBC_STDBIT_DISPATCH(stdc_count_zeros, x)
|
||||
#define stdc_count_ones(x) VLIBC_STDBIT_DISPATCH(stdc_count_ones, x)
|
||||
#define stdc_has_single_bit(x) VLIBC_STDBIT_DISPATCH(stdc_has_single_bit, x)
|
||||
#define stdc_bit_width(x) VLIBC_STDBIT_DISPATCH(stdc_bit_width, x)
|
||||
#define stdc_bit_floor(x) VLIBC_STDBIT_DISPATCH(stdc_bit_floor, x)
|
||||
#define stdc_bit_ceil(x) VLIBC_STDBIT_DISPATCH(stdc_bit_ceil, x)
|
||||
|
||||
#endif /* VLIBC_STDBIT_H */
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef VLIBC_STDBOOL_H
|
||||
#define VLIBC_STDBOOL_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdbool.h>.
|
||||
*
|
||||
* In C23 bool, true and false are keywords, so this header defines only the
|
||||
* guard macro __bool_true_false_are_defined. In C17 (and older) it defines
|
||||
* the _Bool-based spellings the standard requires. In C++ the three names are
|
||||
* keywords as well, so only the guard macro is defined there too.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifndef __cplusplus
|
||||
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L
|
||||
#define bool _Bool
|
||||
#define true 1
|
||||
#define false 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define __bool_true_false_are_defined 1
|
||||
|
||||
#endif /* VLIBC_STDBOOL_H */
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef VLIBC_STDCKDINT_H
|
||||
#define VLIBC_STDCKDINT_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdckdint.h>.
|
||||
*
|
||||
* Checked integer arithmetic (C23). Each macro computes the operation and
|
||||
* stores the result through *result; it returns true when the mathematical
|
||||
* result is not representable in the result type, in which case *result is
|
||||
* left unmodified. The implementation defers to the GCC
|
||||
* __builtin_*_overflow family, which is type-generic across every integer
|
||||
* type, matching the standard's semantics.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#define ckd_add(result, a, b) __builtin_add_overflow((a), (b), (result))
|
||||
#define ckd_sub(result, a, b) __builtin_sub_overflow((a), (b), (result))
|
||||
#define ckd_mul(result, a, b) __builtin_mul_overflow((a), (b), (result))
|
||||
|
||||
#endif /* VLIBC_STDCKDINT_H */
|
||||
+11
-2
@@ -20,11 +20,15 @@ typedef __SIZE_TYPE__ size_t;
|
||||
/* Signed integer type of the difference of two pointers. */
|
||||
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
||||
|
||||
/* Wide character type. */
|
||||
/* Wide character type; a C++ keyword there, so no redefinition. */
|
||||
#ifndef __cplusplus
|
||||
typedef __WCHAR_TYPE__ wchar_t;
|
||||
#endif
|
||||
|
||||
/* Type of the null pointer constant nullptr (C23). */
|
||||
/* Type of the null pointer constant nullptr (C23); a C++ keyword there. */
|
||||
#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
|
||||
typedef typeof(nullptr) nullptr_t;
|
||||
#endif
|
||||
|
||||
/* Null pointer constant. */
|
||||
#define NULL ((void *)0)
|
||||
@@ -32,6 +36,11 @@ typedef typeof(nullptr) nullptr_t;
|
||||
/* Offset in bytes of a member from the start of its enclosing object. */
|
||||
#define offsetof(type, m) __builtin_offsetof(type, m)
|
||||
|
||||
/* Mark the following point of the program as unreachable (C23). */
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L
|
||||
#define unreachable() __builtin_unreachable()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An object type whose alignment is as great as that of any supported
|
||||
* fundamental type; suitable as the storage type for aligned allocation.
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#ifndef VLIBC_STDINT_H
|
||||
#define VLIBC_STDINT_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdint.h>.
|
||||
*
|
||||
* Fixed-width integer types and their limit and constant macros (C23). Every
|
||||
* definition is derived from compiler predefined macros (__INT8_TYPE__,
|
||||
* __INT64_MAX__, __INT64_C, ...), so this header is fully self-contained and
|
||||
* never borrows from a system header. Widths follow the x86_64 LP64 model.
|
||||
*
|
||||
* The compatibility profile is exposed by include/vlibc/features.h; this
|
||||
* header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
/* Exact-width types. */
|
||||
typedef __INT8_TYPE__ int8_t;
|
||||
typedef __INT16_TYPE__ int16_t;
|
||||
typedef __INT32_TYPE__ int32_t;
|
||||
typedef __INT64_TYPE__ int64_t;
|
||||
typedef __UINT8_TYPE__ uint8_t;
|
||||
typedef __UINT16_TYPE__ uint16_t;
|
||||
typedef __UINT32_TYPE__ uint32_t;
|
||||
typedef __UINT64_TYPE__ uint64_t;
|
||||
|
||||
/* Types with at least the given width (least-width). */
|
||||
typedef __INT_LEAST8_TYPE__ int_least8_t;
|
||||
typedef __INT_LEAST16_TYPE__ int_least16_t;
|
||||
typedef __INT_LEAST32_TYPE__ int_least32_t;
|
||||
typedef __INT_LEAST64_TYPE__ int_least64_t;
|
||||
typedef __UINT_LEAST8_TYPE__ uint_least8_t;
|
||||
typedef __UINT_LEAST16_TYPE__ uint_least16_t;
|
||||
typedef __UINT_LEAST32_TYPE__ uint_least32_t;
|
||||
typedef __UINT_LEAST64_TYPE__ uint_least64_t;
|
||||
|
||||
/* Fastest types with at least the given width. */
|
||||
typedef __INT_FAST8_TYPE__ int_fast8_t;
|
||||
typedef __INT_FAST16_TYPE__ int_fast16_t;
|
||||
typedef __INT_FAST32_TYPE__ int_fast32_t;
|
||||
typedef __INT_FAST64_TYPE__ int_fast64_t;
|
||||
typedef __UINT_FAST8_TYPE__ uint_fast8_t;
|
||||
typedef __UINT_FAST16_TYPE__ uint_fast16_t;
|
||||
typedef __UINT_FAST32_TYPE__ uint_fast32_t;
|
||||
typedef __UINT_FAST64_TYPE__ uint_fast64_t;
|
||||
|
||||
/* Types wide enough for pointers. */
|
||||
typedef __INTPTR_TYPE__ intptr_t;
|
||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
||||
|
||||
/* Types of maximum width. */
|
||||
typedef __INTMAX_TYPE__ intmax_t;
|
||||
typedef __UINTMAX_TYPE__ uintmax_t;
|
||||
|
||||
/* Limits of the exact-width types. */
|
||||
#define INT8_MIN (-__INT8_MAX__ - 1)
|
||||
#define INT8_MAX __INT8_MAX__
|
||||
#define UINT8_MAX __UINT8_MAX__
|
||||
#define INT16_MIN (-__INT16_MAX__ - 1)
|
||||
#define INT16_MAX __INT16_MAX__
|
||||
#define UINT16_MAX __UINT16_MAX__
|
||||
#define INT32_MIN (-__INT32_MAX__ - 1)
|
||||
#define INT32_MAX __INT32_MAX__
|
||||
#define UINT32_MAX __UINT32_MAX__
|
||||
#define INT64_MIN (-__INT64_MAX__ - 1)
|
||||
#define INT64_MAX __INT64_MAX__
|
||||
#define UINT64_MAX __UINT64_MAX__
|
||||
|
||||
/* Limits of the least-width types. */
|
||||
#define INT_LEAST8_MIN (-__INT_LEAST8_MAX__ - 1)
|
||||
#define INT_LEAST8_MAX __INT_LEAST8_MAX__
|
||||
#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__
|
||||
#define INT_LEAST16_MIN (-__INT_LEAST16_MAX__ - 1)
|
||||
#define INT_LEAST16_MAX __INT_LEAST16_MAX__
|
||||
#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__
|
||||
#define INT_LEAST32_MIN (-__INT_LEAST32_MAX__ - 1)
|
||||
#define INT_LEAST32_MAX __INT_LEAST32_MAX__
|
||||
#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__
|
||||
#define INT_LEAST64_MIN (-__INT_LEAST64_MAX__ - 1)
|
||||
#define INT_LEAST64_MAX __INT_LEAST64_MAX__
|
||||
#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__
|
||||
|
||||
/* Limits of the fastest types. */
|
||||
#define INT_FAST8_MIN (-__INT_FAST8_MAX__ - 1)
|
||||
#define INT_FAST8_MAX __INT_FAST8_MAX__
|
||||
#define UINT_FAST8_MAX __UINT_FAST8_MAX__
|
||||
#define INT_FAST16_MIN (-__INT_FAST16_MAX__ - 1)
|
||||
#define INT_FAST16_MAX __INT_FAST16_MAX__
|
||||
#define UINT_FAST16_MAX __UINT_FAST16_MAX__
|
||||
#define INT_FAST32_MIN (-__INT_FAST32_MAX__ - 1)
|
||||
#define INT_FAST32_MAX __INT_FAST32_MAX__
|
||||
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
|
||||
#define INT_FAST64_MIN (-__INT_FAST64_MAX__ - 1)
|
||||
#define INT_FAST64_MAX __INT_FAST64_MAX__
|
||||
#define UINT_FAST64_MAX __UINT_FAST64_MAX__
|
||||
|
||||
/* Limits of the pointer-width and maximum-width types. */
|
||||
#define INTPTR_MIN (-__INTPTR_MAX__ - 1)
|
||||
#define INTPTR_MAX __INTPTR_MAX__
|
||||
#define UINTPTR_MAX __UINTPTR_MAX__
|
||||
#define INTMAX_MIN (-__INTMAX_MAX__ - 1)
|
||||
#define INTMAX_MAX __INTMAX_MAX__
|
||||
#define UINTMAX_MAX __UINTMAX_MAX__
|
||||
|
||||
/* Limits of other standard integer types (x86_64: wchar_t is signed int). */
|
||||
#define WCHAR_MIN __WCHAR_MIN__
|
||||
#define WCHAR_MAX __WCHAR_MAX__
|
||||
|
||||
/* Macros for integer constants of the given type. */
|
||||
#define INT8_C(c) __INT8_C(c)
|
||||
#define INT16_C(c) __INT16_C(c)
|
||||
#define INT32_C(c) __INT32_C(c)
|
||||
#define INT64_C(c) __INT64_C(c)
|
||||
#define UINT8_C(c) __UINT8_C(c)
|
||||
#define UINT16_C(c) __UINT16_C(c)
|
||||
#define UINT32_C(c) __UINT32_C(c)
|
||||
#define UINT64_C(c) __UINT64_C(c)
|
||||
#define INTMAX_C(c) __INTMAX_C(c)
|
||||
#define UINTMAX_C(c) __UINTMAX_C(c)
|
||||
|
||||
/* Width of each integer type, in bits (C23). */
|
||||
#define INT8_WIDTH 8
|
||||
#define INT16_WIDTH 16
|
||||
#define INT32_WIDTH 32
|
||||
#define INT64_WIDTH 64
|
||||
#define UINT8_WIDTH 8
|
||||
#define UINT16_WIDTH 16
|
||||
#define UINT32_WIDTH 32
|
||||
#define UINT64_WIDTH 64
|
||||
#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
||||
#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
||||
#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
||||
#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
||||
#define UINT_LEAST8_WIDTH __UINT_LEAST8_WIDTH__
|
||||
#define UINT_LEAST16_WIDTH __UINT_LEAST16_WIDTH__
|
||||
#define UINT_LEAST32_WIDTH __UINT_LEAST32_WIDTH__
|
||||
#define UINT_LEAST64_WIDTH __UINT_LEAST64_WIDTH__
|
||||
#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
||||
#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
||||
#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
||||
#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
||||
#define UINT_FAST8_WIDTH __UINT_FAST8_WIDTH__
|
||||
#define UINT_FAST16_WIDTH __UINT_FAST16_WIDTH__
|
||||
#define UINT_FAST32_WIDTH __UINT_FAST32_WIDTH__
|
||||
#define UINT_FAST64_WIDTH __UINT_FAST64_WIDTH__
|
||||
#define INTPTR_WIDTH __INTPTR_WIDTH__
|
||||
#define UINTPTR_WIDTH __UINTPTR_WIDTH__
|
||||
#define INTMAX_WIDTH __INTMAX_WIDTH__
|
||||
#define UINTMAX_WIDTH __UINTMAX_WIDTH__
|
||||
#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__
|
||||
#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__
|
||||
#define SIZE_WIDTH __SIZE_WIDTH__
|
||||
#define WCHAR_WIDTH __WCHAR_WIDTH__
|
||||
#define WINT_WIDTH __WINT_WIDTH__
|
||||
|
||||
#endif /* VLIBC_STDINT_H */
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
#ifndef VLIBC_STDIO_H
|
||||
#define VLIBC_STDIO_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdio.h>.
|
||||
*
|
||||
* Buffered stream I/O (todo 15). The FILE object is opaque to consumers;
|
||||
* the layout lives in src/stdio/stdio_impl.h. stdin/stdout/stderr are
|
||||
* pre-wired streams over fds 0/1/2 and are initialized lazily on first
|
||||
* use. On Linux there is no text/binary distinction, so the 'b' mode
|
||||
* character is accepted and ignored.
|
||||
*
|
||||
* Buffering modes for setvbuf: _IOFBF (fully buffered), _IOLBF (line
|
||||
* buffered: flush on newline), _IONBF (unbuffered). A stream opened on a
|
||||
* terminal defaults to line buffering for stdout and full buffering for
|
||||
* stdin; stderr is always unbuffered.
|
||||
*
|
||||
* getc/putc/getchar/putchar are declared as functions (address-taking and
|
||||
* #undef work) and additionally defined as macros over fgetc/fputc; the
|
||||
* macro arguments are evaluated exactly once.
|
||||
*
|
||||
* Level 2 (muslmimic) adds tmpnam/ctermid/setbuffer/setlinebuf and the
|
||||
* fopen64 name alias (identical ABI on LP64).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The opaque stream object. The underlying struct tag is vlibc_FILE (see
|
||||
* src/stdio/stdio_impl.h); consumers only ever use FILE *.
|
||||
*/
|
||||
typedef struct vlibc_FILE FILE;
|
||||
|
||||
/* The three pre-wired standard streams (fds 0, 1, 2). */
|
||||
extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
|
||||
/* End of file indicator for character functions. */
|
||||
#define EOF (-1)
|
||||
|
||||
/* Minimum number of simultaneously open files. */
|
||||
#define FOPEN_MAX 16
|
||||
|
||||
/* Default buffer size for setvbuf/setbuf. */
|
||||
#define BUFSIZ 8192
|
||||
|
||||
/* Maximum length of a path argument for stdio functions. */
|
||||
#define FILENAME_MAX 4096
|
||||
|
||||
/* Minimum number of distinct tmpnam-generated names. */
|
||||
#define TMP_MAX 238328
|
||||
|
||||
/* Buffer sizes for tmpnam and ctermid results. */
|
||||
#define L_tmpnam 20
|
||||
#define L_ctermid 9
|
||||
|
||||
/* Seek positions for fseek/fseeko. */
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
/* setvbuf modes. */
|
||||
#define _IOFBF 0 // NOLINT(bugprone-reserved-identifier)
|
||||
#define _IOLBF 1 // NOLINT(bugprone-reserved-identifier)
|
||||
#define _IONBF 2 // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
/* Opaque file position type for fgetpos/fsetpos. */
|
||||
typedef off_t fpos_t;
|
||||
|
||||
/*
|
||||
* Open the file at path with the given mode. The mode is 'r', 'w', or 'a',
|
||||
* optionally followed by '+' (update: read and write), 'b' (ignored on
|
||||
* Linux), and/or 'x' (exclusive create, C11). Returns NULL with errno set
|
||||
* on failure.
|
||||
*/
|
||||
FILE *
|
||||
fopen(const char *restrict path, const char *restrict mode);
|
||||
|
||||
/*
|
||||
* Wrap an existing descriptor in a stream. The requested mode must be
|
||||
* compatible with the descriptor's access mode (basic check via fcntl).
|
||||
* The descriptor is not duplicated; fclose closes it.
|
||||
*/
|
||||
FILE *
|
||||
fdopen(int fd, const char *mode);
|
||||
|
||||
/*
|
||||
* Rebind stream to path. The old descriptor is flushed and closed first.
|
||||
* With path == NULL only the mode changes and the descriptor stays open.
|
||||
* Returns NULL with errno set on failure.
|
||||
*/
|
||||
FILE *
|
||||
freopen(const char *restrict path, const char *restrict mode, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Flush pending output, close the descriptor, and release the stream.
|
||||
* Returns EOF if flushing or closing failed.
|
||||
*/
|
||||
int
|
||||
fclose(FILE *stream);
|
||||
|
||||
/*
|
||||
* Flush pending output of stream. fflush(NULL) flushes all open streams
|
||||
* with pending output. On a stream with no pending writes (read mode) the
|
||||
* unread buffered data is discarded and the position rewound; this never
|
||||
* corrupts the stream. Returns EOF on error.
|
||||
*/
|
||||
int
|
||||
fflush(FILE *stream);
|
||||
|
||||
/*
|
||||
* Set the buffer of stream to buf. buf == NULL selects unbuffered I/O;
|
||||
* otherwise the buffer is used with full buffering and BUFSIZ size. Must
|
||||
* be called before the first operation on the stream.
|
||||
*/
|
||||
void
|
||||
setbuf(FILE *restrict stream, char *restrict buf);
|
||||
|
||||
/*
|
||||
* Set the buffering mode of stream: _IOFBF, _IOLBF, or _IONBF. With
|
||||
* buf != NULL the caller supplies size bytes of storage; with buf == NULL
|
||||
* the buffer is allocated lazily on first use (size is then ignored,
|
||||
* except that _IONBF needs no buffer). Returns 0, or -1 with errno EINVAL
|
||||
* for an invalid mode or a non-NULL buf with size 0 (except _IONBF).
|
||||
*/
|
||||
int
|
||||
setvbuf(FILE *restrict stream, char *restrict buf, int mode, size_t size);
|
||||
|
||||
/*
|
||||
* Read up to size * nmemb bytes in items of size bytes each. Returns the
|
||||
* number of complete items read; fewer than nmemb means end of file or an
|
||||
* error (distinguishable via feof/ferror). size or nmemb zero returns 0
|
||||
* without touching the stream.
|
||||
*/
|
||||
size_t
|
||||
fread(void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Write size * nmemb bytes in items of size bytes each. Returns the number
|
||||
* of complete items written; fewer than nmemb means an error (see
|
||||
* ferror).
|
||||
*/
|
||||
size_t
|
||||
fwrite(const void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream);
|
||||
|
||||
/* Read the next character as unsigned char, or EOF. */
|
||||
int
|
||||
fgetc(FILE *stream);
|
||||
|
||||
/* Write c as unsigned char; returns it, or EOF on error. */
|
||||
int
|
||||
fputc(int c, FILE *stream);
|
||||
|
||||
/* Same as fgetc/fputc; also provided as macros (arguments evaluated once). */
|
||||
int
|
||||
getc(FILE *stream);
|
||||
|
||||
int
|
||||
putc(int c, FILE *stream);
|
||||
|
||||
int
|
||||
getchar(void);
|
||||
|
||||
int
|
||||
putchar(int c);
|
||||
|
||||
/*
|
||||
* Read at most n-1 characters into s, stopping after (and keeping) a
|
||||
* newline, then NUL-terminate. Returns s, or NULL if no character was read
|
||||
* (end of file or error).
|
||||
*/
|
||||
char *
|
||||
fgets(char *restrict s, int n, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Write the NUL-terminated string s to stream (no trailing newline is
|
||||
* added). Returns a non-negative value, or EOF on error.
|
||||
*/
|
||||
int
|
||||
fputs(const char *restrict s, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Push c back onto the input stream; the next read returns it again. One
|
||||
* byte of pushback is guaranteed. Returns c, or EOF on error (also for
|
||||
* ungetc(EOF)). A successful seek discards the pushed-back character.
|
||||
*/
|
||||
int
|
||||
ungetc(int c, FILE *stream);
|
||||
|
||||
/* Position the stream (see SEEK_SET/SEEK_CUR/SEEK_END); clears feof. */
|
||||
int
|
||||
fseek(FILE *stream, long offset, int whence);
|
||||
|
||||
int
|
||||
fseeko(FILE *stream, off_t offset, int whence);
|
||||
|
||||
/* Current stream position, -1 with errno set on error. */
|
||||
long
|
||||
ftell(FILE *stream);
|
||||
|
||||
/*
|
||||
* Not marked pure: the implementation can allocate the stream buffer on
|
||||
* first use (an observable side effect), so the compiler must not elide
|
||||
* or reorder the call. (feof/ferror/fileno below are genuinely read-only
|
||||
* and keep their pure attribute.)
|
||||
*/
|
||||
off_t
|
||||
ftello(FILE *stream);
|
||||
|
||||
/* Rewind to the start and clear feof/ferror (equivalent to
|
||||
* fseeko(stream, 0, SEEK_SET) + clearerr). */
|
||||
void
|
||||
rewind(FILE *stream);
|
||||
|
||||
/* Get/set the opaque position via fpos_t. */
|
||||
int
|
||||
fgetpos(FILE *restrict stream, fpos_t *restrict pos);
|
||||
|
||||
int
|
||||
fsetpos(FILE *stream, const fpos_t *pos);
|
||||
|
||||
/* End-of-file and error indicators. */
|
||||
__attribute__((pure)) int
|
||||
feof(FILE *stream);
|
||||
|
||||
__attribute__((pure)) int
|
||||
ferror(FILE *stream);
|
||||
|
||||
void
|
||||
clearerr(FILE *stream);
|
||||
|
||||
/*
|
||||
* Remove the file at path (a directory is removed like rmdir). Returns 0,
|
||||
* or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
remove(const char *path);
|
||||
|
||||
/* Rename oldpath to newpath. Returns 0, or -1 with errno set. */
|
||||
int
|
||||
rename(const char *oldpath, const char *newpath);
|
||||
|
||||
/*
|
||||
* Create an anonymous temporary file ("w+b"): the file is created in /tmp
|
||||
* and unlinked immediately, so it disappears on close. Returns the stream,
|
||||
* or NULL with errno set.
|
||||
*/
|
||||
FILE *
|
||||
tmpfile(void);
|
||||
|
||||
/* The descriptor underlying the stream. */
|
||||
__attribute__((pure)) int
|
||||
fileno(FILE *stream);
|
||||
|
||||
/*
|
||||
* Run command in a subshell ("sh -c command") with a pipe attached to
|
||||
* its standard output (mode "r") or standard input (mode "w"). Only the
|
||||
* two POSIX modes are accepted (the glibc "re"/"we" close-on-exec
|
||||
* extension is not). Returns the stream, or NULL with errno set.
|
||||
* pclose closes the stream, waits for the shell, and returns its
|
||||
* termination status (the raw wait status, e.g. 0 for "exit 0"), or -1
|
||||
* when the stream was not opened by popen or the wait failed.
|
||||
*/
|
||||
FILE *
|
||||
popen(const char *command, const char *mode);
|
||||
|
||||
int
|
||||
pclose(FILE *stream);
|
||||
|
||||
/* getc/putc/getchar/putchar as macros over fgetc/fputc (see above). */
|
||||
#define getc(stream) fgetc(stream)
|
||||
#define putc(c, stream) fputc((c), (stream))
|
||||
#define getchar() fgetc(stdin)
|
||||
#define putchar(c) fputc((c), stdout)
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Generate a name for a temporary file ("/tmp/vlibcXXXXXX" form; the file
|
||||
* is NOT created). With s == NULL a static buffer is used. Not thread-safe,
|
||||
* obsolescent.
|
||||
*/
|
||||
char *
|
||||
tmpnam(char *s);
|
||||
|
||||
/* Controlling terminal path: copies "/dev/tty" into s (or a static
|
||||
* buffer when s == NULL) and returns it. */
|
||||
char *
|
||||
ctermid(char *s);
|
||||
|
||||
/* BSD: like setvbuf with _IOFBF/_IONBF and the given size. */
|
||||
void
|
||||
setbuffer(FILE *stream, char *buf, size_t size);
|
||||
|
||||
/* BSD: select line buffering (setvbuf with _IOLBF and NULL buffer). */
|
||||
void
|
||||
setlinebuf(FILE *stream);
|
||||
|
||||
/* glibc LFS name alias: identical to fopen on LP64. */
|
||||
FILE *
|
||||
fopen64(const char *restrict path, const char *restrict mode);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* formatted output (todo 16) */
|
||||
|
||||
int
|
||||
printf(const char *restrict format, ...);
|
||||
int
|
||||
fprintf(FILE *restrict stream, const char *restrict format, ...);
|
||||
int
|
||||
sprintf(char *restrict s, const char *restrict format, ...);
|
||||
int
|
||||
snprintf(char *restrict s, size_t n, const char *restrict format, ...);
|
||||
int
|
||||
vprintf(const char *restrict format, va_list ap);
|
||||
int
|
||||
vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
|
||||
int
|
||||
vsprintf(char *restrict s, const char *restrict format, va_list ap);
|
||||
int
|
||||
vsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap);
|
||||
int
|
||||
dprintf(int fd, const char *restrict format, ...);
|
||||
int
|
||||
vdprintf(int fd, const char *restrict format, va_list ap);
|
||||
void
|
||||
perror(const char *s);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
int
|
||||
asprintf(char **restrict strp, const char *restrict format, ...);
|
||||
int
|
||||
vasprintf(char **restrict strp, const char *restrict format, va_list ap);
|
||||
#endif
|
||||
|
||||
/* line input, stream locking, and memory streams (todo 18) */
|
||||
|
||||
/*
|
||||
* Read one line from stream into a malloc'd, NUL-terminated buffer. On the
|
||||
* first call *lineptr may be NULL (a buffer is allocated); *n is the buffer
|
||||
* capacity and grows as needed. The newline is kept in the output; embedded
|
||||
* NUL bytes are preserved (the length comes from the return value, not
|
||||
* strlen). Returns the number of bytes read including the delimiter, or -1
|
||||
* at end of file (nothing read) or on error.
|
||||
*/
|
||||
ssize_t
|
||||
getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream);
|
||||
|
||||
/*
|
||||
* Like getline, but reads up to the given delimiter byte (which is kept in
|
||||
* the output). Reading stops at end of file without the delimiter; a partial
|
||||
* final line is still returned with its byte count.
|
||||
*/
|
||||
ssize_t
|
||||
getdelim(char **restrict lineptr, size_t *restrict n, int delim, FILE *restrict stream);
|
||||
|
||||
/* Write s to stdout followed by a newline. Returns a non-negative value,
|
||||
* or EOF on error. */
|
||||
int
|
||||
puts(const char *s);
|
||||
|
||||
/*
|
||||
* Per-stream advisory locks (POSIX.1-2008 base). flockfile is recursive:
|
||||
* the owning thread may lock the same stream any number of times, and
|
||||
* funlockfile must be called once per successful flockfile. ftrylockfile
|
||||
* acquires without blocking: 0 on success, nonzero when the stream is
|
||||
* already locked. Single-threaded today; the lock fields become real
|
||||
* blocking locks when the thread runtime lands.
|
||||
*/
|
||||
void
|
||||
flockfile(FILE *stream);
|
||||
|
||||
int
|
||||
ftrylockfile(FILE *stream);
|
||||
|
||||
void
|
||||
funlockfile(FILE *stream);
|
||||
|
||||
/*
|
||||
* Unlocked character I/O: the stream must already be locked by the caller
|
||||
* (see flockfile). Identical to fgetc/fputc over stdin/stdout without
|
||||
* taking the per-stream lock.
|
||||
*/
|
||||
int
|
||||
getc_unlocked(FILE *stream);
|
||||
|
||||
int
|
||||
getchar_unlocked(void);
|
||||
|
||||
int
|
||||
putc_unlocked(int c, FILE *stream);
|
||||
|
||||
int
|
||||
putchar_unlocked(int c);
|
||||
|
||||
/*
|
||||
* Open a stream over the memory region buf of size bytes. With buf == NULL
|
||||
* the stream allocates and owns a growable buffer of its own that is freed
|
||||
* on fclose. Modes are fopen-like ('r', 'w', 'a', optional '+' and 'b',
|
||||
* where 'b' is accepted but on Linux only disables the string semantics):
|
||||
* 'w' truncates the current length to zero, 'a' positions at the end of the
|
||||
* data currently in the buffer (the first NUL byte in string mode), and
|
||||
* writing past the buffer size is an error. Returns the stream, or NULL
|
||||
* with errno set.
|
||||
*/
|
||||
FILE *
|
||||
fmemopen(void *buf, size_t size, const char *mode);
|
||||
|
||||
/*
|
||||
* Open a write-only dynamic memory stream. The stream owns a growable
|
||||
* buffer; after fflush or fclose the buffer is NUL-terminated at the current
|
||||
* data length, *ptr points at it (it may have moved), and *sizeloc holds the
|
||||
* length. The caller releases the buffer with free() after fclose.
|
||||
*/
|
||||
FILE *
|
||||
open_memstream(char **ptr, size_t *sizeloc);
|
||||
|
||||
/* formatted input (todo 17) */
|
||||
|
||||
/*
|
||||
* Read formatted input. scanf reads from stdin, fscanf from the stream,
|
||||
* sscanf from the string. The conversion directives are %d %i %u %o %x %X
|
||||
* %f %F %e %E %g %G %a %A %c %s %[ %p %n and %% (with assignment
|
||||
* suppression '*', a decimal field width, and the length modifiers hh h l
|
||||
* ll j z t, plus L for the float conversions). A conversion fails to match
|
||||
* without consuming its offending character; the return value is the
|
||||
* number of assigned (non-suppressed) input items, or EOF if an input
|
||||
* failure occurs before the first one. Integer overflow stores a saturated
|
||||
* value and sets errno = ERANGE (nothing else touches errno).
|
||||
*/
|
||||
int
|
||||
scanf(const char *restrict format, ...);
|
||||
|
||||
int
|
||||
fscanf(FILE *restrict stream, const char *restrict format, ...);
|
||||
|
||||
int
|
||||
sscanf(const char *restrict s, const char *restrict format, ...);
|
||||
|
||||
int
|
||||
vscanf(const char *restrict format, va_list ap);
|
||||
|
||||
int
|
||||
vfscanf(FILE *restrict stream, const char *restrict format, va_list ap);
|
||||
|
||||
int
|
||||
vsscanf(const char *restrict s, const char *restrict format, va_list ap);
|
||||
|
||||
#endif /* VLIBC_STDIO_H */
|
||||
@@ -0,0 +1,566 @@
|
||||
#ifndef VLIBC_STDLIB_H
|
||||
#define VLIBC_STDLIB_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdlib.h>.
|
||||
*
|
||||
* This header is the shared home for the stdlib declarations; it currently
|
||||
* holds only the memory-management family (todo 7). Later todos extend it
|
||||
* in place: todo 11 adds the numeric conversions (atoi/strtol/strtod/...),
|
||||
* todo 12 the pseudo-random and search/divide functions
|
||||
* (rand/srand/qsort/bsearch/abs/div/...), and todo 13 the environment and
|
||||
* multibyte helpers (getenv/setenv/mblen/mbtowc/...).
|
||||
*
|
||||
* Memory management functions, gated by the active compatibility profile
|
||||
* (see include/vlibc/features.h). Levels are cumulative:
|
||||
*
|
||||
* Level 1 (onlyposix): ISO C core + POSIX base — malloc, free, calloc,
|
||||
* realloc, aligned_alloc, posix_memalign.
|
||||
* Level 2 (muslmimic): malloc_usable_size (BSD/musl).
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gates below always
|
||||
* see the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first, and <stddef.h> for size_t.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Level 1: memory management (always present). */
|
||||
|
||||
/*
|
||||
* Allocate size bytes, 16-byte aligned. The memory is uninitialized.
|
||||
* malloc(0) returns a unique minimum-size block (never NULL on success).
|
||||
* NULL + errno ENOMEM on failure.
|
||||
* malloc: the result does not alias any other pointer and has size bytes.
|
||||
*/
|
||||
__attribute__((malloc, alloc_size(1))) void *
|
||||
malloc(size_t size);
|
||||
|
||||
/*
|
||||
* Release the block at ptr, which must be NULL or a value returned by an
|
||||
* earlier allocation in this family. free(NULL) is a no-op.
|
||||
*/
|
||||
void
|
||||
free(void *ptr);
|
||||
|
||||
/*
|
||||
* Allocate an array of nmemb elements of size bytes each, all bits zero.
|
||||
* The product is overflow-checked: on overflow NULL + errno ENOMEM.
|
||||
* calloc with a zero product returns a unique zeroed minimum-size block.
|
||||
* malloc: the result does not alias any other pointer and has nmemb*size
|
||||
* bytes.
|
||||
*/
|
||||
__attribute__((malloc, alloc_size(1, 2))) void *
|
||||
calloc(size_t nmemb, size_t size);
|
||||
|
||||
/*
|
||||
* Resize the block at ptr to size bytes, preserving the first min(old,
|
||||
* size) bytes. realloc(NULL, size) behaves as malloc(size); realloc(ptr, 0)
|
||||
* frees ptr and returns NULL. The old block is freed on success and left
|
||||
* untouched on failure (NULL + errno ENOMEM).
|
||||
* alloc_size(2): the result has size bytes.
|
||||
*/
|
||||
__attribute__((alloc_size(2))) void *
|
||||
realloc(void *ptr, size_t size);
|
||||
|
||||
/*
|
||||
* Allocate size bytes aligned to alignment. alignment must be a power of
|
||||
* two that is a multiple of sizeof(void *), and size must be a multiple of
|
||||
* alignment; a violation fails with NULL + errno EINVAL (a non-power-of-two
|
||||
* alignment is undefined behavior in C23, so only well-formed arguments
|
||||
* reach the allocator). size 0 returns NULL. The result is released with
|
||||
* free.
|
||||
* malloc + alloc_align(1): the result does not alias any other pointer and
|
||||
* is aligned to alignment.
|
||||
*/
|
||||
__attribute__((malloc, alloc_size(2), alloc_align(1))) void *
|
||||
aligned_alloc(size_t alignment, size_t size);
|
||||
|
||||
/*
|
||||
* Allocate size bytes at address alignment and store the result in
|
||||
* *memptr. alignment must be a power of two and a multiple of
|
||||
* sizeof(void *). Returns 0 on success, EINVAL for a bad alignment, ENOMEM
|
||||
* on allocation failure. Never sets errno itself, and never modifies
|
||||
* *memptr on failure. size 0 returns a unique minimum-size block.
|
||||
*/
|
||||
__attribute__((access(write_only, 1))) int
|
||||
posix_memalign(void **memptr, size_t alignment, size_t size);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): BSD/musl extensions. */
|
||||
|
||||
/*
|
||||
* Return the number of bytes actually available in the block at ptr,
|
||||
* including any internal padding; at least as large as the requested size.
|
||||
* ptr may be any block returned by the allocator family; NULL returns 0.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) size_t
|
||||
malloc_usable_size(void *ptr);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* numeric conversions (todo 11) */
|
||||
|
||||
/*
|
||||
* String-to-number conversions (C23 7.24.1). All are ISO C core / POSIX
|
||||
* base and present in every profile.
|
||||
*
|
||||
* The ato* wrappers carry no error reporting: their behavior is undefined
|
||||
* if the converted value cannot be represented (C23 7.24.1.1-2), so they
|
||||
* read their input and nothing else — hence pure.
|
||||
*
|
||||
* The strto* functions report range errors through errno and store the
|
||||
* scan position through endptr; both are real side effects, so they carry
|
||||
* no intent attribute. errno behavior follows C23 7.24.1.4-5: ERANGE when
|
||||
* the subject sequence is outside the representable range (the clamped
|
||||
* maximum/minimum is returned); EINVAL when base is not 0 and not in
|
||||
* [2, 36] (endptr left at nptr, 0 returned); endptr points at nptr when
|
||||
* no subject sequence is present.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Convert the initial decimal digits of nptr to int, discarding leading
|
||||
* whitespace and an optional sign. Equivalent to (int)strtol(nptr, 0, 10).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
atoi(const char *nptr);
|
||||
|
||||
/*
|
||||
* As atoi, converted to long: strtol(nptr, 0, 10).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) long
|
||||
atol(const char *nptr);
|
||||
|
||||
/*
|
||||
* As atoi, converted to long long: strtoll(nptr, 0, 10).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) long long
|
||||
atoll(const char *nptr);
|
||||
|
||||
/*
|
||||
* As atoi, converted to double: strtod(nptr, 0).
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) double
|
||||
atof(const char *nptr);
|
||||
|
||||
/*
|
||||
* Convert the initial portion of nptr to long, stopping at the first
|
||||
* character that is not part of the subject sequence and storing its
|
||||
* position in *endptr (if endptr is not NULL). See the family comment for
|
||||
* base, errno, and endptr semantics.
|
||||
*/
|
||||
long
|
||||
strtol(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* As strtol, converted to unsigned long. A subject sequence with a minus
|
||||
* sign yields the negated value computed in the return type (modulo
|
||||
* ULONG_MAX + 1) without a range error (C23 7.24.1.4p8).
|
||||
*/
|
||||
unsigned long
|
||||
strtoul(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* As strtol, converted to long long.
|
||||
*/
|
||||
long long
|
||||
strtoll(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* As strtol, converted to unsigned long long. Negative subject sequences
|
||||
* wrap modulo ULLONG_MAX + 1 as for strtoul.
|
||||
*/
|
||||
unsigned long long
|
||||
strtoull(const char *restrict nptr, char **restrict endptr, int base);
|
||||
|
||||
/*
|
||||
* Convert the initial portion of nptr to float, double, or long double
|
||||
* (C23 7.24.1.3): optional whitespace, optional sign, then either an
|
||||
* "inf"/"infinity" or "nan"/"nan(n-char-sequence)" subject (case-
|
||||
* insensitive), a hexadecimal floating subject ("0x1.8p1"), or a decimal
|
||||
* floating subject with optional exponent. Overflow returns ±HUGE_VAL*
|
||||
* with errno ERANGE; results too small to represent return a subnormal or
|
||||
* zero value with errno ERANGE. no-conversion stores nptr in *endptr.
|
||||
*/
|
||||
double
|
||||
strtod(const char *restrict nptr, char **restrict endptr);
|
||||
|
||||
float
|
||||
strtof(const char *restrict nptr, char **restrict endptr);
|
||||
|
||||
long double
|
||||
strtold(const char *restrict nptr, char **restrict endptr);
|
||||
|
||||
/* pseudo-random numbers, search, and integer arithmetic (todo 12) */
|
||||
|
||||
/*
|
||||
* C23 7.22.2/7.22.5/7.22.6: rand/srand, qsort/bsearch, the abs family, and
|
||||
* the div family are ISO C core and present in every profile. The abs and
|
||||
* div functions compute a pure function of their arguments, so they are
|
||||
* declared const (the compiler folds and eliminates the calls in static
|
||||
* links; GCC's own builtin declarations of abs/labs/llabs are const, and
|
||||
* the attribute here matches them). rand and srand carry state, and
|
||||
* qsort/bsearch call a caller-supplied comparator, so none of those four
|
||||
* carries an intent attribute.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The largest value rand() returns: 2^31 - 1, the largest int.
|
||||
*/
|
||||
#define RAND_MAX 2147483647
|
||||
|
||||
/*
|
||||
* Quotient/remainder pair from div/ldiv/lldiv: quot is the algebraic
|
||||
* quotient truncated toward zero, rem the remainder of the same sign as
|
||||
* the dividend, and quot*denom + rem == num.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int quot;
|
||||
int rem;
|
||||
} div_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long quot;
|
||||
long rem;
|
||||
} ldiv_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long long quot;
|
||||
long long rem;
|
||||
} lldiv_t;
|
||||
|
||||
/*
|
||||
* Absolute value of n. The most-negative value returns itself: the
|
||||
* negation happens in the unsigned type, which wraps, so the result is
|
||||
* never undefined behavior and errno is never set.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
abs(int n);
|
||||
|
||||
__attribute__((const)) long
|
||||
labs(long n);
|
||||
|
||||
__attribute__((const)) long long
|
||||
llabs(long long n);
|
||||
|
||||
/*
|
||||
* Quotient and remainder of num/denom, truncated toward zero.
|
||||
*/
|
||||
__attribute__((const)) div_t
|
||||
div(int num, int denom);
|
||||
|
||||
__attribute__((const)) ldiv_t
|
||||
ldiv(long num, long denom);
|
||||
|
||||
__attribute__((const)) lldiv_t
|
||||
lldiv(long long num, long long denom);
|
||||
|
||||
/*
|
||||
* Pseudo-random integer in [0, RAND_MAX], deterministic for a given
|
||||
* srand seed.
|
||||
*/
|
||||
int
|
||||
rand(void);
|
||||
|
||||
/*
|
||||
* Seed the rand() sequence with seed.
|
||||
*/
|
||||
void
|
||||
srand(unsigned int seed);
|
||||
|
||||
/*
|
||||
* Sort the array of nmemb elements of size bytes at base into ascending
|
||||
* order according to compar (C23 7.22.5.2). compar receives two pointers
|
||||
* to distinct elements and returns negative/zero/positive. The sort is
|
||||
* not stable.
|
||||
*/
|
||||
void
|
||||
qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
|
||||
|
||||
/*
|
||||
* Binary-search the array of nmemb sorted elements of size bytes at base
|
||||
* for *key, calling compar(key, element) (C23 7.22.5.1). Returns a
|
||||
* pointer to the matching element, or NULL when there is none.
|
||||
*/
|
||||
void *
|
||||
bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and obsolescent extensions. */
|
||||
|
||||
/*
|
||||
* Reentrant rand: the state lives in the caller's *seedp, which is
|
||||
* updated on every call, so the sequence is independent of rand()'s own
|
||||
* global state.
|
||||
*/
|
||||
int
|
||||
rand_r(unsigned int *seedp);
|
||||
|
||||
/*
|
||||
* random/srandom family (XSI): a 31-bit pseudo-random sequence in
|
||||
* [0, 2^31). initstate installs state (size bytes, at least
|
||||
* sizeof(long)) as the current state buffer, seeds it, and returns the
|
||||
* previous buffer; setstate installs the buffer and returns the previous
|
||||
* one. srandom reseeds the current buffer.
|
||||
*/
|
||||
long
|
||||
random(void);
|
||||
|
||||
void
|
||||
srandom(unsigned int seed);
|
||||
|
||||
char *
|
||||
initstate(unsigned int seed, char *state, size_t size);
|
||||
|
||||
char *
|
||||
setstate(char *state);
|
||||
|
||||
/*
|
||||
* drand48 family (XSI): a 48-bit linear congruential sequence carried in
|
||||
* three unsigned shorts, least-significant first. drand48/erand48 return
|
||||
* the current value divided by 2^48 as a double in [0, 1); lrand48/
|
||||
* nrand48 return the high 31 bits; mrand48/jrand48 the high 32 bits
|
||||
* sign-extended. The erand48/nrand48/jrand48 forms step the caller's
|
||||
* xsubi in place; srand48/seed48/lcong48 manage the shared state and the
|
||||
* multiplier/addend.
|
||||
*/
|
||||
double
|
||||
drand48(void);
|
||||
|
||||
double
|
||||
erand48(unsigned short xsubi[3]);
|
||||
|
||||
long
|
||||
lrand48(void);
|
||||
|
||||
long
|
||||
nrand48(unsigned short xsubi[3]);
|
||||
|
||||
long
|
||||
mrand48(void);
|
||||
|
||||
long
|
||||
jrand48(unsigned short xsubi[3]);
|
||||
|
||||
void
|
||||
srand48(long seedval);
|
||||
|
||||
unsigned short *
|
||||
seed48(unsigned short seed16v[3]);
|
||||
|
||||
void
|
||||
lcong48(unsigned short param[7]);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* environment, multibyte characters, and temporary files (todo 13) */
|
||||
|
||||
/*
|
||||
* getenv/setenv/unsetenv (POSIX base), the C-locale multibyte conversions
|
||||
* (C23 7.24.7), and mkstemp/mkdtemp are level 1. The XSI/BSD extras —
|
||||
* putenv, clearenv, mktemp, the PTY helpers, and getsubopt — are level 2.
|
||||
*
|
||||
* None of these functions carries an intent attribute. getenv and the
|
||||
* environment mutators all read or write the shared global environ, so
|
||||
* pure/const would let the compiler hoist a stale getenv result across a
|
||||
* setenv call. The multibyte conversions are locale-stateful in general,
|
||||
* the temporary-file functions create filesystem entries, and ptsname
|
||||
* writes a shared static buffer — none of them is side-effect-free.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The process environment: a NULL-terminated array of "name=value"
|
||||
* strings. The array is installed by the startup code before main; the
|
||||
* functions below mutate it (setenv/unsetenv copy strings into malloc'd
|
||||
* storage). Reassigning environ directly is permitted by POSIX, but
|
||||
* calling setenv/unsetenv/putenv/clearenv afterwards is undefined, as it
|
||||
* is everywhere (the array is then no longer guaranteed to be owned by
|
||||
* the library).
|
||||
*/
|
||||
extern char **environ;
|
||||
|
||||
/*
|
||||
* Return a pointer to the value part (the bytes after '=') of the
|
||||
* environment entry whose name matches name, or NULL when the variable
|
||||
* is not set. The pointer is valid until the next setenv, unsetenv,
|
||||
* putenv, or clearenv call. No intent attribute: the result depends on
|
||||
* the mutable global environ.
|
||||
*/
|
||||
char *
|
||||
getenv(const char *name);
|
||||
|
||||
/*
|
||||
* Set name to value. A copy of "name=value" is made, so the caller may
|
||||
* reuse or free its buffers once the call returns. With overwrite
|
||||
* nonzero an existing entry is replaced; with overwrite zero an existing
|
||||
* entry is left unchanged and 0 is returned. Returns -1 with errno
|
||||
* EINVAL when name is NULL, empty, or contains '=', and -1 with errno
|
||||
* ENOMEM on allocation failure. A NULL value is treated as the empty
|
||||
* string.
|
||||
*/
|
||||
int
|
||||
setenv(const char *name, const char *value, int overwrite);
|
||||
|
||||
/*
|
||||
* Remove every entry whose name is name, compacting the array. Returns
|
||||
* -1 with errno EINVAL when name is NULL, empty, or contains '='.
|
||||
*/
|
||||
int
|
||||
unsetenv(const char *name);
|
||||
|
||||
/*
|
||||
* C-locale multibyte conversions (C23 7.24.7). Only the "C" locale
|
||||
* exists today (locale support is a later todo), where every multibyte
|
||||
* character is a single byte: byte c corresponds to the wide character
|
||||
* (unsigned char)c. The encoding is therefore stateless — the s == NULL
|
||||
* and NULL-destination forms all report "state-independent" — and no
|
||||
* mbstate_t variant is needed. mbtowc never fails in this locale (every
|
||||
* byte, including 0x80..0xFF, is a valid character); wctomb fails with
|
||||
* -1 + errno EILSEQ for any wchar_t outside 0..255.
|
||||
*/
|
||||
int
|
||||
mblen(const char *s, size_t n);
|
||||
|
||||
int
|
||||
mbtowc(wchar_t *restrict pwc, const char *restrict s, size_t n);
|
||||
|
||||
int
|
||||
wctomb(char *s, wchar_t wc);
|
||||
|
||||
size_t
|
||||
mbstowcs(wchar_t *restrict pwcs, const char *restrict s, size_t n);
|
||||
|
||||
size_t
|
||||
wcstombs(char *restrict s, const wchar_t *restrict pwcs, size_t n);
|
||||
|
||||
/*
|
||||
* Temporary files and directories. mkstemp replaces the trailing
|
||||
* "XXXXXX" of the template with random characters, creates the file
|
||||
* with mode 0600, and returns an open file descriptor (-1 with errno
|
||||
* EINVAL when the template has no "XXXXXX" suffix). mkdtemp does the
|
||||
* same replacement, creates a directory with mode 0700, and returns the
|
||||
* template (NULL on failure). The parameter is unnamed in these
|
||||
* declarations because POSIX's `template` is a C++ keyword and this
|
||||
* header is compiled by C++ consumers too.
|
||||
*/
|
||||
int
|
||||
mkstemp(char *);
|
||||
|
||||
char *
|
||||
mkdtemp(char *);
|
||||
|
||||
/* process control (todo 20) */
|
||||
|
||||
/*
|
||||
* Pass string to the command language interpreter: "sh -c string".
|
||||
* Returns the shell's wait status (e.g. 768 for "exit 3"), 1 when
|
||||
* string is NULL (a shell is always available), or -1 with errno set
|
||||
* when the child cannot be created or reaped. During the run, SIGCHLD
|
||||
* is blocked and SIGINT/SIGQUIT are ignored in the caller, as POSIX
|
||||
* requires.
|
||||
*/
|
||||
int
|
||||
system(const char *string);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI and BSD environment extras. */
|
||||
|
||||
/*
|
||||
* Add string — a "name=value" pair in CALLER-OWNED storage — to the
|
||||
* environment: the caller's buffer becomes part of the environment and
|
||||
* must remain valid for the life of the process. An existing variable of
|
||||
* the same name is replaced (the old pointer is dropped); a string
|
||||
* without '=' removes the variable instead (glibc/musl behavior).
|
||||
* Returns 0, or -1 with errno ENOMEM.
|
||||
*/
|
||||
int
|
||||
putenv(char *string);
|
||||
|
||||
/*
|
||||
* Empty the environment, leaving environ as a fresh valid empty array
|
||||
* (never NULL) so later setenv calls keep working. The previous array
|
||||
* and its entries are deliberately not freed — entries may be putenv'd
|
||||
* caller storage or initial-stack strings. Returns 0, or -1 with errno
|
||||
* ENOMEM.
|
||||
*/
|
||||
int
|
||||
clearenv(void);
|
||||
|
||||
/*
|
||||
* Obsolescent mkstemp without the file: fills in the trailing "XXXXXX"
|
||||
* and returns the template, creating nothing. The name is not guaranteed
|
||||
* unique — nothing exists to race on. NULL with errno EINVAL when the
|
||||
* template has no "XXXXXX" suffix.
|
||||
*/
|
||||
char *
|
||||
mktemp(char *);
|
||||
|
||||
/*
|
||||
* XSI PTY helpers. posix_openpt opens the master side of a
|
||||
* pseudo-terminal via /dev/ptmx (flags, plus O_NOCTTY, are passed to
|
||||
* open) and returns the fd. grantpt returns 0 without doing anything:
|
||||
* modern kernels grant the slave when the master is opened. unlockpt
|
||||
* releases the slave-side lock. ptsname formats the slave path
|
||||
* "/dev/pts/N" into a shared static buffer — the result is valid until
|
||||
* the next ptsname call and is not thread-safe.
|
||||
*/
|
||||
int
|
||||
posix_openpt(int flags);
|
||||
|
||||
char *
|
||||
ptsname(int fd);
|
||||
|
||||
int
|
||||
grantpt(int fd);
|
||||
|
||||
int
|
||||
unlockpt(int fd);
|
||||
|
||||
/*
|
||||
* Parse one comma-separated suboption out of *optionp (POSIX XSI). On
|
||||
* success the index of the matching token is returned and *valuep points
|
||||
* to the "=value" part (or is NULL when the suboption has no '='); for
|
||||
* an unrecognized suboption -1 is returned and *valuep points to the
|
||||
* whole suboption string; at the end of the list -1 is returned and
|
||||
* *valuep is NULL. *optionp always advances past the comma, which is
|
||||
* overwritten with NUL (the string is modified in place).
|
||||
*/
|
||||
int
|
||||
getsubopt(char **optionp, char *const *tokens, char **valuep);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Resolve path to an absolute, NUL-terminated canonical pathname with no
|
||||
* symbolic links, "." or ".." components (todo 38). resolved_path may be
|
||||
* NULL, in which case the canonical path is returned in a malloc'd
|
||||
* buffer the caller must free; otherwise it must hold at least PATH_MAX
|
||||
* bytes. Returns resolved_path (or the malloc'd buffer), or NULL with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
char *
|
||||
realpath(const char *restrict path, char *restrict resolved_path);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_STDLIB_H */
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef VLIBC_STDNORETURN_H
|
||||
#define VLIBC_STDNORETURN_H
|
||||
|
||||
/*
|
||||
* vlibc — <stdnoreturn.h>.
|
||||
*
|
||||
* In C23 noreturn is a keyword, so this header has nothing left to define.
|
||||
* In C17 (and older) it maps noreturn onto the _Noreturn function specifier.
|
||||
* In C++ [[noreturn]] is an attribute, not a macro, so nothing is defined
|
||||
* there either.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifndef __cplusplus
|
||||
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L
|
||||
#define noreturn _Noreturn
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_STDNORETURN_H */
|
||||
+226
-2
@@ -7,8 +7,14 @@
|
||||
* String and memory functions, gated by the active compatibility profile
|
||||
* (see include/vlibc/features.h). Levels are cumulative:
|
||||
*
|
||||
* Level 1 (onlyposix): ISO C core — memcpy, memmove, memset, strlen, strcmp.
|
||||
* Level 2 (muslmimic): BSD extensions — strlcpy, strlcat.
|
||||
* Level 1 (onlyposix): ISO C core + POSIX.1-2008 base — memcpy, memmove,
|
||||
* memset, memchr, memcmp, strlen, strnlen, strcmp,
|
||||
* strncmp, strcpy, strncpy, strcat, strncat, strchr,
|
||||
* strrchr, strspn, strcspn, strpbrk, strstr, strtok,
|
||||
* strtok_r, strcoll, strxfrm, strdup, strndup,
|
||||
* strsignal.
|
||||
* Level 2 (muslmimic): BSD + XSI extensions — strlcpy, strlcat, stpcpy,
|
||||
* stpncpy, memccpy, strcoll_l, strxfrm_l.
|
||||
* Level 3 (muslext): GNU extensions — strcasestr.
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gates below always
|
||||
@@ -33,6 +39,15 @@ extern "C" {
|
||||
__attribute__((pure)) size_t
|
||||
strlen(const char *s);
|
||||
|
||||
/*
|
||||
* Return the length of s, excluding the terminating NUL, examining at most
|
||||
* maxlen bytes; the result is at most maxlen even when s is not a proper
|
||||
* NUL-terminated string.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) size_t
|
||||
strnlen(const char *s, size_t maxlen);
|
||||
|
||||
/*
|
||||
* Compare the strings lhs and rhs; return negative, zero, or positive when
|
||||
* lhs is less than, equal to, or greater than rhs.
|
||||
@@ -41,6 +56,24 @@ strlen(const char *s);
|
||||
__attribute__((pure)) int
|
||||
strcmp(const char *lhs, const char *rhs);
|
||||
|
||||
/*
|
||||
* Compare at most n bytes of lhs and rhs, stopping early at the first
|
||||
* difference or the first NUL; return negative, zero, or positive when lhs
|
||||
* is less than, equal to, or greater than rhs.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
strncmp(const char *lhs, const char *rhs, size_t n);
|
||||
|
||||
/*
|
||||
* Compare s1 and s2 under the active locale's collating sequence; return
|
||||
* negative, zero, or positive. Only the "C" locale exists so far (locales
|
||||
* are owned by a later todo), where collation is identical to strcmp.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
strcoll(const char *s1, const char *s2);
|
||||
|
||||
/*
|
||||
* Copy n bytes from src to dst. The regions must not overlap (restrict).
|
||||
* No intent attribute: it writes memory.
|
||||
@@ -63,6 +96,157 @@ memmove(void *dst, const void *src, size_t n);
|
||||
void *
|
||||
memset(void *dst, int c, size_t n);
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of c (converted to unsigned
|
||||
* char) among the first n bytes of s, or NULL when absent.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) void *
|
||||
memchr(const void *s, int c, size_t n);
|
||||
|
||||
/*
|
||||
* Compare the first n bytes of lhs and rhs as unsigned char; return
|
||||
* negative, zero, or positive when lhs is less than, equal to, or greater
|
||||
* than rhs. Unlike strcmp, a NUL byte does not end the comparison.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
memcmp(const void *lhs, const void *rhs, size_t n);
|
||||
|
||||
/*
|
||||
* Copy src to dst, including the terminating NUL; return dst. The strings
|
||||
* must not overlap (restrict).
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
char *
|
||||
strcpy(char *restrict dst, const char *restrict src);
|
||||
|
||||
/*
|
||||
* Copy at most n bytes from src to dst. When src is shorter than n the
|
||||
* remainder of dst is NUL-padded; when src is n bytes or longer the result
|
||||
* is not NUL-terminated. Return dst.
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
char *
|
||||
strncpy(char *restrict dst, const char *restrict src, size_t n);
|
||||
|
||||
/*
|
||||
* Append src (including its NUL) to the end of dst; return dst. The strings
|
||||
* must not overlap (restrict).
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
char *
|
||||
strcat(char *restrict dst, const char *restrict src);
|
||||
|
||||
/*
|
||||
* Append at most n bytes of src to dst and always NUL-terminate; return
|
||||
* dst.
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
char *
|
||||
strncat(char *restrict dst, const char *restrict src, size_t n);
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of c (converted to char) in s,
|
||||
* or NULL when absent. The terminating NUL is part of the string, so
|
||||
* strchr(s, '\0') returns a pointer to it.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) char *
|
||||
strchr(const char *s, int c);
|
||||
|
||||
/*
|
||||
* Return a pointer to the last occurrence of c (converted to char) in s, or
|
||||
* NULL when absent. The terminating NUL is part of the string, so
|
||||
* strrchr(s, '\0') returns a pointer to it.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) char *
|
||||
strrchr(const char *s, int c);
|
||||
|
||||
/*
|
||||
* Return the length of the initial span of s consisting entirely of bytes
|
||||
* that occur in accept.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) size_t
|
||||
strspn(const char *s, const char *accept);
|
||||
|
||||
/*
|
||||
* Return the length of the initial span of s consisting entirely of bytes
|
||||
* that do NOT occur in reject.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) size_t
|
||||
strcspn(const char *s, const char *reject);
|
||||
|
||||
/*
|
||||
* Return a pointer to the first byte in s that also occurs in accept, or
|
||||
* NULL when none occurs.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) char *
|
||||
strpbrk(const char *s, const char *accept);
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of needle in haystack, or NULL
|
||||
* when absent. An empty needle matches haystack itself.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) char *
|
||||
strstr(const char *haystack, const char *needle);
|
||||
|
||||
/*
|
||||
* Split s into tokens delimited by any byte from sep. On the first call s
|
||||
* names the string; subsequent calls with NULL continue the same string.
|
||||
* Leading and consecutive delimiters produce no empty tokens, and the
|
||||
* delimiter bytes in s are overwritten with NUL. Returns NULL when no token
|
||||
* remains. strtok keeps its state in private static storage and is not
|
||||
* thread-safe; strtok_r keeps it in *state and is.
|
||||
* No intent attribute: strtok mutates private state, strtok_r writes
|
||||
* through its parameters.
|
||||
*/
|
||||
char *
|
||||
strtok(char *restrict s, const char *restrict sep);
|
||||
|
||||
char *
|
||||
strtok_r(char *restrict s, const char *restrict sep, char **restrict state);
|
||||
|
||||
/*
|
||||
* Transform src under the active locale's collating sequence so that strcmp
|
||||
* on transformed strings orders them as strcoll would, storing at most n
|
||||
* bytes of the result in dst (always NUL-terminated when n > 0; nothing is
|
||||
* written when n == 0). Return the length of the full transformed string,
|
||||
* excluding the NUL. In the "C" locale the transformation is the identity
|
||||
* and the return is strlen(src).
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
size_t
|
||||
strxfrm(char *restrict dst, const char *restrict src, size_t n);
|
||||
|
||||
/*
|
||||
* Return a heap copy of s (malloc-allocated; release with free), or NULL
|
||||
* with errno ENOMEM on allocation failure. strndup copies at most n bytes
|
||||
* and NUL-terminates, so strndup(s, 0) returns the empty string.
|
||||
* malloc: returns fresh unaliased storage the caller owns.
|
||||
*/
|
||||
__attribute__((malloc)) char *
|
||||
strdup(const char *s);
|
||||
|
||||
__attribute__((malloc)) char *
|
||||
strndup(const char *s, size_t n);
|
||||
|
||||
/*
|
||||
* Return a pointer to a static string describing the signal sig, or a
|
||||
* formatted "Unknown signal <sig>" string for unrecognized numbers. Never
|
||||
* returns NULL; distinct known signals yield distinct strings. The text for
|
||||
* signal 0 is unspecified by POSIX and deliberately not pinned.
|
||||
*
|
||||
* No intent attribute: the unknown-signal path writes shared storage.
|
||||
*/
|
||||
char *
|
||||
strsignal(int sig);
|
||||
|
||||
#if VLIBC_LEVEL >= 2
|
||||
/* Level 2 (muslmimic): BSD extensions. */
|
||||
|
||||
@@ -83,6 +267,46 @@ size_t
|
||||
strlcat(char *dst, const char *src, size_t size);
|
||||
#endif /* VLIBC_LEVEL >= 2 */
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI extensions. */
|
||||
|
||||
/*
|
||||
* strcpy/strncpy variants returning a pointer to the terminating NUL of
|
||||
* dst: stpcpy to the NUL of the copied string, stpncpy to the first NUL
|
||||
* written (or to dst + n when no NUL was written).
|
||||
* No intent attribute: they write memory.
|
||||
*/
|
||||
char *
|
||||
stpcpy(char *restrict dst, const char *restrict src);
|
||||
|
||||
char *
|
||||
stpncpy(char *restrict dst, const char *restrict src, size_t n);
|
||||
|
||||
/*
|
||||
* Copy at most n bytes from src to dst, stopping after the first byte equal
|
||||
* to c (converted to unsigned char); return a pointer to the byte after c
|
||||
* in dst when found, NULL otherwise. The regions must not overlap
|
||||
* (restrict).
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
void *
|
||||
memccpy(void *restrict dst, const void *restrict src, int c, size_t n);
|
||||
|
||||
/*
|
||||
* Locale-parameterized XSI variants of strcoll/strxfrm. No locale machinery
|
||||
* exists yet, so the locale argument is accepted and ignored and behavior
|
||||
* is the "C" locale behavior of the base functions. The locale parameter is
|
||||
* typed void * for now: locale_t will be an ABI-identical pointer typedef
|
||||
* defined by <locale.h>, whose todo updates these signatures to the real
|
||||
* type.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
strcoll_l(const char *s1, const char *s2, void *locale);
|
||||
|
||||
size_t
|
||||
strxfrm_l(char *restrict dst, const char *restrict src, size_t n, void *locale);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#if VLIBC_LEVEL >= 3
|
||||
/* Level 3 (muslext): GNU extensions. */
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef VLIBC_STRINGS_H
|
||||
#define VLIBC_STRINGS_H
|
||||
|
||||
/*
|
||||
* vlibc — <strings.h>.
|
||||
*
|
||||
* BSD/XSI legacy string functions, gated by the active compatibility
|
||||
* profile (see include/vlibc/features.h). This ENTIRE header is a legacy
|
||||
* extension: nothing it declares is POSIX.1-2008 base, so at level 1
|
||||
* (onlyposix) it is empty. POSIX mandates that <strings.h> (BSD legacy)
|
||||
* and <string.h> (ISO C) remain separate headers; the two never share a
|
||||
* declaration, so including both can never conflict.
|
||||
*
|
||||
* strcasecmp / strncasecmp — XSI case-insensitive comparisons (ASCII
|
||||
* 'A'..'Z'/'a'..'z' fold only, byte-wise);
|
||||
* ffs / ffsl / ffsll — XSI find-first-set-bit, 1-based;
|
||||
* bcmp / bcopy / bzero — BSD legacy byte operations (bcopy takes
|
||||
* (src, dst) — the arguments are REVERSED
|
||||
* relative to memcpy/memmove);
|
||||
* index / rindex — BSD legacy names for strchr / strrchr.
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gate below always
|
||||
* sees the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first, and <stddef.h> for size_t.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compare s1 and s2 byte-wise, ignoring case: each byte is read as
|
||||
* unsigned char and an ASCII 'A'..'Z' is folded to 'a'..'z' before the
|
||||
* comparison; bytes at or above 0x80 pass through unmodified. Return
|
||||
* negative, zero, or positive when s1 is less than, equal to, or greater
|
||||
* than s2.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
strcasecmp(const char *s1, const char *s2);
|
||||
|
||||
/*
|
||||
* Compare at most n bytes of s1 and s2 as strcasecmp does, stopping early
|
||||
* at the first difference or the first NUL; return negative, zero, or
|
||||
* positive like strcasecmp. The NUL terminator of either string ends the
|
||||
* comparison even when n is larger.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
/*
|
||||
* Return the 1-based index of the least significant set bit of i, or 0
|
||||
* when i has no set bit: ffs(0) == 0, ffs(1) == 1, ffs(8) == 4,
|
||||
* ffs(INT_MIN) == 32.
|
||||
* const: the result depends only on i.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
ffs(int i);
|
||||
|
||||
/*
|
||||
* Same as ffs for long: ffsl(0) == 0, ffsl(1L << 40) == 41,
|
||||
* ffsl(LONG_MIN) == 64 (long is 64-bit on x86_64).
|
||||
* const: the result depends only on i.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
ffsl(long i);
|
||||
|
||||
/*
|
||||
* Same as ffs for long long: ffsll(0) == 0, ffsll(LLONG_MIN) == 64.
|
||||
* const: the result depends only on i.
|
||||
*/
|
||||
__attribute__((const)) int
|
||||
ffsll(long long i);
|
||||
|
||||
/*
|
||||
* Compare the first n bytes of s1 and s2 as unsigned char, like memcmp;
|
||||
* unlike strcmp a NUL byte does not end the comparison. Return 0 when the
|
||||
* n bytes are equal, nonzero otherwise.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) int
|
||||
bcmp(const void *s1, const void *s2, size_t n);
|
||||
|
||||
/*
|
||||
* Copy n bytes from src to dst. The regions may overlap and the copy
|
||||
* behaves like memmove. NOTE the argument order: source first, destination
|
||||
* second — the reverse of memcpy/memmove.
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
void
|
||||
bcopy(const void *src, void *dst, size_t n);
|
||||
|
||||
/*
|
||||
* Fill n bytes at s with zero.
|
||||
* No intent attribute: it writes memory.
|
||||
*/
|
||||
void
|
||||
bzero(void *s, size_t n);
|
||||
|
||||
/*
|
||||
* Return a pointer to the first occurrence of c (converted to unsigned
|
||||
* char) in s, or NULL when absent. The terminating NUL is part of the
|
||||
* string, so index(s, '\0') returns a pointer to it. Legacy name for
|
||||
* strchr.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) char *
|
||||
index(const char *s, int c);
|
||||
|
||||
/*
|
||||
* Return a pointer to the last occurrence of c (converted to unsigned
|
||||
* char) in s, or NULL when absent. The terminating NUL is part of the
|
||||
* string, so rindex(s, '\0') returns a pointer to it. Legacy name for
|
||||
* strrchr.
|
||||
* pure: reads memory, no side effects.
|
||||
*/
|
||||
__attribute__((pure)) char *
|
||||
rindex(const char *s, int c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_STRINGS_H */
|
||||
@@ -0,0 +1,216 @@
|
||||
#ifndef VLIBC_SYS_MMAN_H
|
||||
#define VLIBC_SYS_MMAN_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/mman.h>.
|
||||
*
|
||||
* Memory mapping, protection, and synchronization (POSIX.1-2008). Every
|
||||
* function here is an unbuffered pass-through to the kernel: failures are
|
||||
* reported as -1 (or MAP_FAILED for mmap) with errno set by the syscall
|
||||
* layer.
|
||||
*
|
||||
* Level 1 (onlyposix): mmap, munmap, mprotect, msync, mlock, munlock,
|
||||
* mlockall, munlockall, posix_madvise.
|
||||
* Level 2 (muslmimic): madvise (XSI — posix_madvise is the POSIX base
|
||||
* form), shm_open, shm_unlink.
|
||||
*
|
||||
* All constant values are Linux x86_64 kernel-UAPI facts (asm-generic/
|
||||
* mman-common.h and mman.h), transcribed, not invented. PROT_*, MAP_*,
|
||||
* MAP_FAILED, MS_* and MCL_* exist at level 1. The POSIX_MADV_* advice
|
||||
* values match the Linux MADV_* values for the same advice (0-4), which is
|
||||
* what lets posix_madvise pass its advice straight to SYS_madvise; the
|
||||
* Linux MADV_* names themselves are gated at level 2 with madvise, the only
|
||||
* function that takes them. MAP_ANONYMOUS is provided as a source
|
||||
* compatibility alias of the primary spelling MAP_ANON (the BSD/POSIX
|
||||
* draft name glibc also accepts).
|
||||
*
|
||||
* POSIX shared memory (shm_open/shm_unlink, level 2) is backed by a real
|
||||
* named file under /dev/shm (tmpfs), NOT by memfd_create: the object is a
|
||||
* path in the shared-memory filesystem, visible there and re-openable by
|
||||
* name until shm_unlink removes it, which matches POSIX's named-object
|
||||
* semantics. vlibc renders name as "/dev/shm/" + name and requires that
|
||||
* name contain no '/' — the object is a single directory entry, so a slash
|
||||
* would escape the namespace (EINVAL). This differs from the POSIX wording
|
||||
* that a name begin with a slash: vlibc names carry no leading slash and
|
||||
* the prefix is added internally. Otherwise shm_open behaves as open(2) on
|
||||
* the rendered path (the oflag access modes plus O_CREAT/O_EXCL/O_TRUNC and
|
||||
* mode bits are honored; O_WRONLY alone is undefined by POSIX and passed
|
||||
* through to the kernel) and shm_unlink as unlink(2) on it.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* changes the caller's address space or performs I/O with side effects and
|
||||
* reports failures through errno, so const/pure would be unsound (the same
|
||||
* rationale unistd.h documents for its I/O family).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- memory protection ---- */
|
||||
|
||||
#define PROT_NONE 0x0 /* page cannot be accessed */
|
||||
#define PROT_READ 0x1 /* page can be read */
|
||||
#define PROT_WRITE 0x2 /* page can be written */
|
||||
#define PROT_EXEC 0x4 /* page can be executed */
|
||||
|
||||
/* ---- mapping types and flags ---- */
|
||||
|
||||
#define MAP_SHARED 0x01 /* share changes with the file and other mappers */
|
||||
#define MAP_PRIVATE 0x02 /* private copy-on-write mapping */
|
||||
#define MAP_FIXED 0x10 /* interpret addr exactly; fail if it cannot be used */
|
||||
#define MAP_ANON 0x20 /* anonymous mapping, not file-backed (primary name) */
|
||||
#define MAP_ANONYMOUS MAP_ANON /* source-compatibility alias of MAP_ANON */
|
||||
|
||||
/* Value returned by mmap on failure (never a valid mapping address). */
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
/* ---- msync synchronization flags ---- */
|
||||
|
||||
#define MS_ASYNC 0x1 /* return before the writes take effect */
|
||||
#define MS_INVALIDATE 0x2 /* invalidate other mappings of the file */
|
||||
#define MS_SYNC 0x4 /* perform synchronous writes */
|
||||
|
||||
/* ---- mlockall flags ---- */
|
||||
|
||||
#define MCL_CURRENT 0x1 /* lock all currently mapped pages */
|
||||
#define MCL_FUTURE 0x2 /* lock all pages mapped in the future */
|
||||
|
||||
/* ---- posix_madvise advice (Linux MADV_* values, see the header note) ---- */
|
||||
|
||||
#define POSIX_MADV_NORMAL 0 /* no special treatment */
|
||||
#define POSIX_MADV_RANDOM 1 /* pages will be accessed randomly */
|
||||
#define POSIX_MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
|
||||
#define POSIX_MADV_WILLNEED 3 /* pages will be needed soon */
|
||||
#define POSIX_MADV_DONTNEED 4 /* pages are not needed soon */
|
||||
|
||||
/*
|
||||
* Map len bytes starting at offset off of the object open on fildes into
|
||||
* the process address space and return the mapping address, or MAP_FAILED
|
||||
* with errno set. addr is a hint for the placement (0 for "anywhere")
|
||||
* unless flags contains MAP_FIXED; prot is a combination of PROT_* (PROT_NONE
|
||||
* alone forbids all access). With MAP_ANON fildes is ignored and must be -1
|
||||
* and off 0; the mapping is zero-filled. The mapping is shared or private
|
||||
* per MAP_SHARED/MAP_PRIVATE. len is rounded up to whole pages.
|
||||
*/
|
||||
void *
|
||||
mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
|
||||
|
||||
/*
|
||||
* Remove the mapping at addr (a page boundary) covering len bytes; return
|
||||
* 0, or -1 with errno set. The address range becomes invalid.
|
||||
*/
|
||||
int
|
||||
munmap(void *addr, size_t len);
|
||||
|
||||
/*
|
||||
* Change the protection of the mapped pages at addr covering len bytes to
|
||||
* prot; return 0, or -1 with errno set. addr must be page-aligned.
|
||||
*/
|
||||
int
|
||||
mprotect(void *addr, size_t len, int prot);
|
||||
|
||||
/*
|
||||
* Flush the mapped pages at addr covering len bytes of a MAP_SHARED mapping
|
||||
* to (or invalidate them from) the underlying object per flags (MS_ASYNC,
|
||||
* MS_SYNC, MS_INVALIDATE); return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
msync(void *addr, size_t len, int flags);
|
||||
|
||||
/*
|
||||
* Lock the pages at addr covering len bytes into memory so they are never
|
||||
* paged out; return 0, or -1 with errno set. munlock unlocks them again.
|
||||
*/
|
||||
int
|
||||
mlock(const void *addr, size_t len);
|
||||
|
||||
int
|
||||
munlock(const void *addr, size_t len);
|
||||
|
||||
/*
|
||||
* Lock all pages mapped by the process into memory per flags (MCL_CURRENT,
|
||||
* MCL_FUTURE); return 0, or -1 with errno set. munlockall unlocks them.
|
||||
*/
|
||||
int
|
||||
mlockall(int flags);
|
||||
|
||||
int
|
||||
munlockall(void);
|
||||
|
||||
/*
|
||||
* Give the kernel advice (one of the POSIX_MADV_* values) about how the
|
||||
* pages at addr covering len bytes will be used; return 0, or -1 with errno
|
||||
* set. The advice may be ignored; the mapping is unchanged.
|
||||
*/
|
||||
int
|
||||
posix_madvise(void *addr, size_t len, int advice);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI + POSIX shared memory. */
|
||||
|
||||
/* ---- madvise advice (Linux x86_64 kernel-UAPI values) ---- */
|
||||
|
||||
#define MADV_NORMAL 0 /* no special treatment */
|
||||
#define MADV_RANDOM 1 /* pages will be accessed randomly */
|
||||
#define MADV_SEQUENTIAL 2 /* pages will be accessed sequentially */
|
||||
#define MADV_WILLNEED 3 /* pages will be needed soon */
|
||||
#define MADV_DONTNEED 4 /* pages are not needed soon; free them */
|
||||
#define MADV_FREE 8 /* free pages; contents lost on later write */
|
||||
#define MADV_REMOVE 9 /* free the pages and punch a hole in the file */
|
||||
#define MADV_DONTFORK 10 /* do not inherit the pages across fork */
|
||||
#define MADV_DOFORK 11 /* revert MADV_DONTFORK */
|
||||
#define MADV_MERGEABLE 12 /* enable KSM merging of the pages */
|
||||
#define MADV_UNMERGEABLE 13 /* revert MADV_MERGEABLE */
|
||||
#define MADV_HUGEPAGE 14 /* prefer transparent huge pages */
|
||||
#define MADV_NOHUGEPAGE 15 /* revert MADV_HUGEPAGE */
|
||||
#define MADV_DONTDUMP 16 /* exclude the pages from core dumps */
|
||||
#define MADV_DODUMP 17 /* revert MADV_DONTDUMP */
|
||||
#define MADV_WIPEONFORK 18 /* zero the pages in the child after fork */
|
||||
#define MADV_KEEPONFORK 19 /* revert MADV_WIPEONFORK */
|
||||
#define MADV_COLD 20 /* pages will be accessed less soon */
|
||||
#define MADV_PAGEOUT 21 /* reclaim the pages immediately */
|
||||
#define MADV_POPULATE_READ 22 /* fault the pages in for reading */
|
||||
#define MADV_POPULATE_WRITE 23 /* fault the pages in for writing */
|
||||
#define MADV_DONTNEED_LOCKED 24 /* like MADV_DONTNEED on locked pages */
|
||||
#define MADV_COLLAPSE 25 /* collapse the range into a THP */
|
||||
|
||||
/*
|
||||
* Linux madvise: give the kernel advice (one of the MADV_* values above)
|
||||
* about the pages at addr covering len bytes; return 0, or -1 with errno
|
||||
* set. XSI — posix_madvise (level 1) is the POSIX base form; this is the
|
||||
* raw Linux interface exposing the full MADV_* set.
|
||||
*/
|
||||
int
|
||||
madvise(void *addr, size_t len, int advice);
|
||||
|
||||
/*
|
||||
* Open (creating with mode if oflag contains O_CREAT) the POSIX shared
|
||||
* memory object name and return a descriptor, or -1 with errno set. The
|
||||
* object is a file under /dev/shm; name must not contain '/', and the
|
||||
* object persists (re-openable by name) until shm_unlink removes it. See
|
||||
* the header note on the naming deviation. oflag behaves as for open(2).
|
||||
*/
|
||||
int
|
||||
shm_open(const char *name, int oflag, mode_t mode);
|
||||
|
||||
/*
|
||||
* Remove the shared memory object name from /dev/shm; return 0, or -1 with
|
||||
* errno set. Open descriptors of the object stay valid until closed. See
|
||||
* the header note on the naming deviation.
|
||||
*/
|
||||
int
|
||||
shm_unlink(const char *name);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_MMAN_H */
|
||||
@@ -0,0 +1,203 @@
|
||||
#ifndef VLIBC_SYS_RESOURCE_H
|
||||
#define VLIBC_SYS_RESOURCE_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/resource.h>.
|
||||
*
|
||||
* Per-process resource limits and usage accounting. Nothing in this header
|
||||
* is POSIX.1-2008 base — getrlimit/setrlimit/getrusage/getpriority/
|
||||
* setpriority and nice are all XSI [CX] — so the entire surface is gated at
|
||||
* level 2:
|
||||
*
|
||||
* Level 2 (muslmimic): getrlimit, setrlimit, getrusage, getpriority,
|
||||
* setpriority, nice, getrlimit64, setrlimit64,
|
||||
* struct rlimit, struct rusage, rlim_t, RLIMIT_*,
|
||||
* RLIM_INFINITY, PRIO_*, RUSAGE_*.
|
||||
*
|
||||
* struct rusage matches the kernel layout on x86_64 verbatim: two struct
|
||||
* timevals (16 bytes each — the kernel record uses the same two-long
|
||||
* __kernel_old_timeval) followed by fourteen longs, and the kernel
|
||||
* getrusage copies it out without translation. struct rlimit is likewise
|
||||
* the kernel's struct rlimit64 (two 64-bit words), which is why
|
||||
* getrlimit/setrlimit pass the record straight to SYS_prlimit64 and why
|
||||
* getrlimit64/setrlimit64 are name-only aliases on this architecture.
|
||||
* Both shapes are pinned by the static asserts below.
|
||||
*
|
||||
* Every function here performs kernel I/O with side effects and reports
|
||||
* failures through errno, so no declaration carries an intent attribute
|
||||
* (const/pure would be unsound).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stddef.h> /* offsetof */
|
||||
|
||||
#include <sys/time.h> /* struct timeval (shared VLIBC_TIMEVAL_DEFINED guard) */
|
||||
|
||||
#include <sys/types.h> /* id_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Resource limit values: the 64-bit width of the kernel's rlimit64 record
|
||||
* (unsigned long long on x86_64, where the classic long is also 64-bit but
|
||||
* the LFS spelling is authoritative).
|
||||
*/
|
||||
typedef unsigned long long rlim_t;
|
||||
|
||||
/*
|
||||
* A resource limit pair: rlim_cur is the soft limit (enforced), rlim_max
|
||||
* the hard limit (ceiling a soft limit may be raised to without
|
||||
* privilege). RLIM_INFINITY in either field means "unlimited".
|
||||
*/
|
||||
struct rlimit
|
||||
{
|
||||
rlim_t rlim_cur; /* soft limit (current) */
|
||||
rlim_t rlim_max; /* hard limit (ceiling) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-process resource usage. The field order is the kernel's own
|
||||
* (getrusage copies the record out untouched): the two CPU-time timevals
|
||||
* first, then the fourteen accounting longs. ru_maxrss is in kilobytes.
|
||||
* The fields a kernel does not maintain stay zero.
|
||||
*/
|
||||
struct rusage
|
||||
{
|
||||
struct timeval ru_utime; /* user CPU time used */
|
||||
struct timeval ru_stime; /* system CPU time used */
|
||||
long ru_maxrss; /* maximum resident set size (KiB) */
|
||||
long ru_ixrss; /* integral shared memory size */
|
||||
long ru_idrss; /* integral unshared data size */
|
||||
long ru_isrss; /* integral unshared stack size */
|
||||
long ru_minflt; /* page reclaims (soft page faults) */
|
||||
long ru_majflt; /* page faults (hard page faults) */
|
||||
long ru_nswap; /* swaps */
|
||||
long ru_inblock; /* block input operations */
|
||||
long ru_oublock; /* block output operations */
|
||||
long ru_msgsnd; /* IPC messages sent */
|
||||
long ru_msgrcv; /* IPC messages received */
|
||||
long ru_nsignals; /* signals received */
|
||||
long ru_nvcsw; /* voluntary context switches */
|
||||
long ru_nivcsw; /* involuntary context switches */
|
||||
};
|
||||
|
||||
/* x86_64 kernel ABI: 2 x struct timeval (16 B) + 14 longs (8 B) = 144 B. */
|
||||
_Static_assert(sizeof(struct rusage) == 144, "struct rusage must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct rusage, ru_maxrss) == 32, "ru_maxrss must follow the two timevals");
|
||||
_Static_assert(sizeof(struct rlimit) == 2 * sizeof(rlim_t),
|
||||
"struct rlimit is a pair of rlim_t words");
|
||||
|
||||
/* ---- Resource limit identifiers (kernel asm-generic values) ---- */
|
||||
|
||||
#define RLIMIT_CPU 0 /* CPU time in seconds */
|
||||
#define RLIMIT_FSIZE 1 /* maximum file size */
|
||||
#define RLIMIT_DATA 2 /* data segment size */
|
||||
#define RLIMIT_STACK 3 /* stack size */
|
||||
#define RLIMIT_CORE 4 /* core file size */
|
||||
#define RLIMIT_RSS 5 /* resident set size */
|
||||
#define RLIMIT_NPROC 6 /* number of processes */
|
||||
#define RLIMIT_NOFILE 7 /* number of open files */
|
||||
#define RLIMIT_MEMLOCK 8 /* locked-in-memory address space */
|
||||
#define RLIMIT_AS 9 /* address space size */
|
||||
#define RLIMIT_LOCKS 10 /* number of file locks held */
|
||||
#define RLIMIT_SIGPENDING 11 /* number of pending signals */
|
||||
#define RLIMIT_MSGQUEUE 12 /* bytes in POSIX message queues */
|
||||
#define RLIMIT_NICE 13 /* ceiling for the nice value */
|
||||
#define RLIMIT_RTPRIO 14 /* maximum realtime priority */
|
||||
#define RLIMIT_RTTIME 15 /* realtime CPU time (us) */
|
||||
#define RLIMIT_NLIMITS 16 /* number of resource kinds */
|
||||
|
||||
/* "No limit" value for either field of struct rlimit. */
|
||||
#define RLIM_INFINITY (~0UL)
|
||||
|
||||
/* Saved-limit markers (legacy; equal to RLIM_INFINITY on Linux). */
|
||||
#define RLIM_SAVED_CUR RLIM_INFINITY
|
||||
#define RLIM_SAVED_MAX RLIM_INFINITY
|
||||
|
||||
/* ---- getpriority/setpriority target selectors ---- */
|
||||
|
||||
#define PRIO_PROCESS 0 /* a single process */
|
||||
#define PRIO_PGRP 1 /* a process group */
|
||||
#define PRIO_USER 2 /* every process of a user */
|
||||
|
||||
/* The valid nice-value range (PRIO_MIN is the highest scheduling priority). */
|
||||
#define PRIO_MIN (-20)
|
||||
#define PRIO_MAX 19
|
||||
|
||||
/* ---- getrusage `who` selectors ---- */
|
||||
|
||||
#define RUSAGE_SELF 0 /* the calling process */
|
||||
#define RUSAGE_CHILDREN (-1) /* terminated and waited-for children */
|
||||
#define RUSAGE_THREAD 1 /* the calling thread */
|
||||
|
||||
/*
|
||||
* Return the current soft and hard limits of resource through rlim. The
|
||||
* call never fails for a valid resource. Return 0, or -1 with errno set
|
||||
* when resource is out of range or rlim points outside the address space.
|
||||
*/
|
||||
int
|
||||
getrlimit(int resource, struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* Set the soft and hard limits of resource from rlim. Raising the hard
|
||||
* limit (or the soft limit above the hard limit) requires privilege.
|
||||
* Return 0, or -1 with errno set for an out-of-range resource, a soft
|
||||
* limit above the hard limit, or a denied raise.
|
||||
*/
|
||||
int
|
||||
setrlimit(int resource, const struct rlimit *rlim);
|
||||
|
||||
/*
|
||||
* Fill usage with the resource usage of who (RUSAGE_SELF, RUSAGE_CHILDREN
|
||||
* or RUSAGE_THREAD). Return 0, or -1 with errno set for an unknown who or
|
||||
* when usage points outside the address space.
|
||||
*/
|
||||
int
|
||||
getrusage(int who, struct rusage *usage);
|
||||
|
||||
/*
|
||||
* Return the nice value of the target described by (which, who), in the
|
||||
* range PRIO_MIN..PRIO_MAX, or -1 with errno set on error. Because -1 is
|
||||
* also a valid priority, callers conventionally clear errno before the
|
||||
* call and treat a -1 return with a nonzero errno as an error.
|
||||
*/
|
||||
int
|
||||
getpriority(int which, id_t who);
|
||||
|
||||
/*
|
||||
* Set the nice value of every process named by (which, who) to prio,
|
||||
* clamping out-of-range values to PRIO_MIN..PRIO_MAX. Lowering the value
|
||||
* (raising priority) requires privilege. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
setpriority(int which, id_t who, int prio);
|
||||
|
||||
/*
|
||||
* Add inc to the calling process's nice value and return the new value,
|
||||
* or -1 with errno set on error (see getpriority for the -1 convention).
|
||||
*/
|
||||
int
|
||||
nice(int inc);
|
||||
|
||||
/*
|
||||
* Large-file aliases. rlim_t is already 64-bit on x86_64, so these behave
|
||||
* exactly as getrlimit/setrlimit and exist as separate exported symbols for
|
||||
* LFS callers.
|
||||
*/
|
||||
int
|
||||
getrlimit64(int resource, struct rlimit *rlim);
|
||||
int
|
||||
setrlimit64(int resource, const struct rlimit *rlim);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_SYS_RESOURCE_H */
|
||||
@@ -0,0 +1,128 @@
|
||||
#ifndef VLIBC_SYS_SELECT_H
|
||||
#define VLIBC_SYS_SELECT_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/select.h>.
|
||||
*
|
||||
* Synchronous I/O multiplexing: wait for readiness on file-descriptor sets
|
||||
* with select() or pselect(). Both are thin wrappers over the SYS_pselect6
|
||||
* kernel ABI, (nfds, readfds, writefds, exceptfds, struct timespec *,
|
||||
* { sigset_t *, size_t } *): pselect passes its timespec and optional signal
|
||||
* mask straight through, select converts its struct timeval timeout to a
|
||||
* timespec and passes a NULL sigset pair.
|
||||
*
|
||||
* Level 1 (onlyposix): select, pselect, FD_* macros, fd_set, struct
|
||||
* timeval.
|
||||
*
|
||||
* fd_set is the kernel's bitmap layout on x86_64: an array of unsigned long
|
||||
* words, with descriptor d held in bit d % 64 of word d / 64. FD_SETSIZE
|
||||
* caps the tracked descriptors at 1024 (16 words), so select()/pselect()
|
||||
* must be called with nfds <= FD_SETSIZE and FD_SET() only ever receives
|
||||
* descriptors below FD_SETSIZE. select() and pselect() report the number of
|
||||
* ready descriptors, 0 on timeout, or -1 with errno set on error.
|
||||
*
|
||||
* The signal mask type is declared here under the shared guard; <signal.h>
|
||||
* is its canonical POSIX home and will own the full sig* API.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Maximum descriptors select()/pselect() track (fd_set's bit capacity). */
|
||||
#define FD_SETSIZE 1024
|
||||
|
||||
/*
|
||||
* A set of file descriptors, one bit per descriptor, stored in the kernel
|
||||
* bitmap layout: an array of unsigned long words, bit d in word
|
||||
* d / (8 * sizeof(unsigned long)). Descriptors 0..FD_SETSIZE-1 fit; no
|
||||
* operation may touch a descriptor at or above FD_SETSIZE.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))];
|
||||
} fd_set;
|
||||
|
||||
/* The word holding bit d of an fd_set, and that bit's mask within the word. */
|
||||
#define VLIBC_FDS_WORD(d) ((d) / (8 * sizeof(unsigned long)))
|
||||
#define VLIBC_FDS_MASK(d) (1UL << ((d) % (8 * sizeof(unsigned long))))
|
||||
|
||||
/* Clear every descriptor bit of set. */
|
||||
#define FD_ZERO(set) \
|
||||
do \
|
||||
{ \
|
||||
size_t fd_zero_i; \
|
||||
for (fd_zero_i = 0; fd_zero_i < sizeof(fd_set) / sizeof(unsigned long); fd_zero_i++) \
|
||||
{ \
|
||||
(set)->fds_bits[fd_zero_i] = 0UL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Add descriptor d to set. */
|
||||
#define FD_SET(d, set) ((set)->fds_bits[VLIBC_FDS_WORD(d)] |= VLIBC_FDS_MASK(d))
|
||||
|
||||
/* Remove descriptor d from set. */
|
||||
#define FD_CLR(d, set) ((set)->fds_bits[VLIBC_FDS_WORD(d)] &= ~VLIBC_FDS_MASK(d))
|
||||
|
||||
/* Nonzero when descriptor d is a member of set. */
|
||||
#define FD_ISSET(d, set) ((set)->fds_bits[VLIBC_FDS_WORD(d)] & VLIBC_FDS_MASK(d))
|
||||
|
||||
#ifndef VLIBC_SIGSET_T_DEFINED
|
||||
#define VLIBC_SIGSET_T_DEFINED
|
||||
/*
|
||||
* Signal mask type: a single 64-bit word — the x86_64 Linux sigset_t (see
|
||||
* setjmp.h). pselect() and ppoll() only ever forward a pointer to the
|
||||
* kernel, which reads the word directly. <signal.h> is the canonical POSIX
|
||||
* home for sigset_t and builds the sig* API on this same layout; the
|
||||
* typedef is repeated in <poll.h> under this guard so the two headers stay
|
||||
* consistent.
|
||||
*/
|
||||
typedef unsigned long sigset_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Elapsed time in seconds and microseconds (see select()'s timeout).
|
||||
* Shared guard with <sys/time.h>, the canonical POSIX home of struct
|
||||
* timeval: both headers define the same layout under
|
||||
* VLIBC_TIMEVAL_DEFINED, so either may be included first at any level.
|
||||
*/
|
||||
#ifndef VLIBC_TIMEVAL_DEFINED
|
||||
#define VLIBC_TIMEVAL_DEFINED
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec;
|
||||
suseconds_t tv_usec;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Wait for readiness on the descriptors marked in readfds, writefds and
|
||||
* exceptfds (each may be NULL), up to nfds descriptors (the highest
|
||||
* descriptor in any set plus one). timeout is an upper bound on the wait; a
|
||||
* NULL timeout blocks indefinitely, { 0, 0 } never blocks. On return each
|
||||
* non-NULL set holds only its ready descriptors. Return the number of ready
|
||||
* descriptors across the sets, 0 on timeout, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
|
||||
|
||||
/*
|
||||
* Like select(), but the timeout is a struct timespec and, when sigmask is
|
||||
* not NULL, the given signal mask is atomically installed for the duration
|
||||
* of the wait (the previous mask is restored before returning).
|
||||
*/
|
||||
int
|
||||
pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||
const struct timespec *timeout, const sigset_t *sigmask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_SELECT_H */
|
||||
@@ -0,0 +1,294 @@
|
||||
#ifndef VLIBC_SYS_STAT_H
|
||||
#define VLIBC_SYS_STAT_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/stat.h>.
|
||||
*
|
||||
* File status: the x86_64 Linux struct stat layout and the S_* mode-bit
|
||||
* macros, plus the stat/mkdir/chmod/utimensat/chown wrapper families
|
||||
* (POSIX.1-2008 base). Every function is an unbuffered pass-through to the
|
||||
* kernel: failures are reported as -1 with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): stat, fstat, lstat, fstatat, mkdir, mkdirat,
|
||||
* mkfifo, mkfifoat, chmod, fchmod, fchmodat, umask,
|
||||
* utimensat, futimens, chown, fchown, lchown,
|
||||
* fchownat (Issue 7 moved lchown from XSI to base).
|
||||
* Level 2 (muslmimic): mknod, mknodat (XSI).
|
||||
*
|
||||
* struct stat matches the x86_64 kernel layout exactly (144 bytes), pinned
|
||||
* by static assertions — the same layout src/stdio/stdio.c transcribed
|
||||
* privately as stdio_stat for its fstat-based isatty(). The
|
||||
* st_atime/st_mtime/st_ctime spellings are glibc-style macros onto
|
||||
* st_atim.tv_sec &c for POSIX source compatibility.
|
||||
*
|
||||
* The AT_* flag constants (AT_FDCWD, AT_SYMLINK_NOFOLLOW, ...) belong to
|
||||
* <fcntl.h> (todo 21) and are deliberately not defined here; the flag
|
||||
* arguments below are plain int and take their values from that header.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provisional struct timespec definition. The canonical home of struct
|
||||
* timespec is <time.h> (todo 41); until it lands, sys/stat.h needs the
|
||||
* definition for the st_*tim members and utimensat/futimens. Todo 41 must
|
||||
* move (or reconcile) this definition — it currently exists nowhere else
|
||||
* (include/threads.h only forward-declares it).
|
||||
*/
|
||||
struct timespec
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* nanoseconds (0..999999999) */
|
||||
};
|
||||
|
||||
/*
|
||||
* File status, x86_64 Linux kernel layout. Field widths are LP64: the
|
||||
* comment column gives the byte offset of each member. The order and the
|
||||
* two padding areas are kernel-ABI facts, not style choices.
|
||||
*/
|
||||
struct stat
|
||||
{
|
||||
dev_t st_dev; /* 0: device containing the file */
|
||||
ino_t st_ino; /* 8: inode number */
|
||||
nlink_t st_nlink; /* 16: hard link count */
|
||||
mode_t st_mode; /* 24: file type + permissions */
|
||||
uid_t st_uid; /* 28: owner user id */
|
||||
gid_t st_gid; /* 32: owner group id */
|
||||
unsigned int st_pad0; /* 36: kernel padding (int __pad0) */
|
||||
dev_t st_rdev; /* 40: device id (if device file) */
|
||||
off_t st_size; /* 48: size in bytes */
|
||||
blksize_t st_blksize; /* 56: preferred I/O block size */
|
||||
blkcnt_t st_blocks; /* 64: 512-byte blocks allocated */
|
||||
struct timespec st_atim; /* 72: last access time */
|
||||
struct timespec st_mtim; /* 88: last modification time */
|
||||
struct timespec st_ctim; /* 104: last status change time */
|
||||
long st_unused[3]; /* 120: kernel padding (__unused) */
|
||||
};
|
||||
|
||||
/* Pin the kernel layout: sizeof and the two offsets stdio consumes. */
|
||||
_Static_assert(sizeof(struct stat) == 144, "struct stat must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct stat, st_mode) == 24, "st_mode must sit at offset 24");
|
||||
_Static_assert(offsetof(struct stat, st_size) == 48, "st_size must sit at offset 48");
|
||||
|
||||
/* File type bits (st_mode & S_IFMT). */
|
||||
#define S_IFMT 0170000 /* type-of-file mask */
|
||||
#define S_IFSOCK 0140000 /* socket */
|
||||
#define S_IFLNK 0120000 /* symbolic link */
|
||||
#define S_IFREG 0100000 /* regular file */
|
||||
#define S_IFBLK 0060000 /* block device */
|
||||
#define S_IFDIR 0040000 /* directory */
|
||||
#define S_IFCHR 0020000 /* character device */
|
||||
#define S_IFIFO 0010000 /* FIFO (named pipe) */
|
||||
|
||||
/* Special permission bits. */
|
||||
#define S_ISUID 04000 /* set-user-id on execution */
|
||||
#define S_ISGID 02000 /* set-group-id on execution */
|
||||
#define S_ISVTX 01000 /* sticky bit (restricted deletion on dirs) */
|
||||
|
||||
/* Owner permission bits. */
|
||||
#define S_IRWXU 0700 /* read, write, execute/search by owner */
|
||||
#define S_IRUSR 0400 /* read permission, owner */
|
||||
#define S_IWUSR 0200 /* write permission, owner */
|
||||
#define S_IXUSR 0100 /* execute/search permission, owner */
|
||||
|
||||
/* Group permission bits. */
|
||||
#define S_IRWXG 070 /* read, write, execute/search by group */
|
||||
#define S_IRGRP 040 /* read permission, group */
|
||||
#define S_IWGRP 020 /* write permission, group */
|
||||
#define S_IXGRP 010 /* execute/search permission, group */
|
||||
|
||||
/* Others permission bits. */
|
||||
#define S_IRWXO 07 /* read, write, execute/search by others */
|
||||
#define S_IROTH 04 /* read permission, others */
|
||||
#define S_IWOTH 02 /* write permission, others */
|
||||
#define S_IXOTH 01 /* execute/search permission, others */
|
||||
|
||||
/* File type predicates over the type bits. */
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||
#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
|
||||
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
|
||||
#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
|
||||
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
|
||||
|
||||
/* POSIX compatibility spellings of the struct timespec members. */
|
||||
#define st_atime st_atim.tv_sec
|
||||
#define st_mtime st_mtim.tv_sec
|
||||
#define st_ctime st_ctim.tv_sec
|
||||
|
||||
/* Level 1 (POSIX base). */
|
||||
|
||||
/*
|
||||
* Store the status of the file named by path into buf. Follows symbolic
|
||||
* links; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
stat(const char *path, struct stat *buf);
|
||||
|
||||
/*
|
||||
* Store the status of the file descriptor fd into buf; return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
fstat(int fd, struct stat *buf);
|
||||
|
||||
/*
|
||||
* Like stat(), but a symbolic link is reported itself, not its target;
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
lstat(const char *path, struct stat *buf);
|
||||
|
||||
/*
|
||||
* Like stat(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD from <fcntl.h> for the current working directory); flag may
|
||||
* hold AT_SYMLINK_NOFOLLOW to report the link itself. Return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
fstatat(int fd, const char *path, struct stat *buf, int flag);
|
||||
|
||||
/*
|
||||
* Create the directory named by path with the access mode mode (masked by
|
||||
* the process umask); return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
mkdir(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Like mkdir(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
mkdirat(int fd, const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Create the FIFO (named pipe) named by path with mode; return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
mkfifo(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Like mkfifo(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
mkfifoat(int fd, const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Change the access mode of the file named by path to mode; return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chmod(const char *path, mode_t mode);
|
||||
|
||||
/*
|
||||
* Change the access mode of the file descriptor fd to mode; return 0, or
|
||||
* -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fchmod(int fd, mode_t mode);
|
||||
|
||||
/*
|
||||
* Like chmod(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); flag may hold
|
||||
* AT_SYMLINK_NOFOLLOW. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fchmodat(int fd, const char *path, mode_t mode, int flag);
|
||||
|
||||
/*
|
||||
* Set the process file-mode creation mask to cmask and return the previous
|
||||
* mask. Never fails.
|
||||
*/
|
||||
mode_t
|
||||
umask(mode_t cmask);
|
||||
|
||||
/*
|
||||
* Set the access and modification times of the file named by path
|
||||
* (relative to fd, or AT_FDCWD) to times[0] (access) and times[1]
|
||||
* (modification). A NULL times sets both to the current time; a tv_nsec
|
||||
* of UTIME_NOW/UTIME_OMIT (from <time.h>) selects per-member behavior.
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
utimensat(int fd, const char *path, const struct timespec times[2], int flag);
|
||||
|
||||
/*
|
||||
* Like utimensat() on the file descriptor fd (the path is implicit);
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
futimens(int fd, const struct timespec times[2]);
|
||||
|
||||
/*
|
||||
* Change the owner and group of the file named by path to owner/group
|
||||
* (a value of (uid_t)-1 leaves the current one unchanged). Follows
|
||||
* symbolic links; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chown(const char *path, uid_t owner, gid_t group);
|
||||
|
||||
/*
|
||||
* Change the owner and group of the file descriptor fd; return 0, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
int
|
||||
fchown(int fd, uid_t owner, gid_t group);
|
||||
|
||||
/*
|
||||
* Like chown(), but a symbolic link is changed itself, not its target;
|
||||
* return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
lchown(const char *path, uid_t owner, gid_t group);
|
||||
|
||||
/*
|
||||
* Like chown(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); flag may hold
|
||||
* AT_SYMLINK_NOFOLLOW. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fchownat(int fd, const char *path, uid_t owner, gid_t group, int flag);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* Create a special file named by path with mode (a file-type bit like
|
||||
* S_IFIFO or S_IFCHR must be set) and device id dev; return 0, or -1 with
|
||||
* errno set. Creating device nodes requires privilege. XSI.
|
||||
*/
|
||||
int
|
||||
mknod(const char *path, mode_t mode, dev_t dev);
|
||||
|
||||
/*
|
||||
* Like mknod(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory); return 0, or -1 with errno
|
||||
* set. XSI.
|
||||
*/
|
||||
int
|
||||
mknodat(int fd, const char *path, mode_t mode, dev_t dev);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_STAT_H */
|
||||
@@ -0,0 +1,82 @@
|
||||
#ifndef VLIBC_SYS_STATVFS_H
|
||||
#define VLIBC_SYS_STATVFS_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/statvfs.h>.
|
||||
*
|
||||
* Filesystem statistics in an ABI-independent POSIX shape: block and file
|
||||
* counts as unsigned counts over a reported block size, plus the read-only
|
||||
* and setuid-disabling mount flags and the longest file name. This header
|
||||
* carries only the portable view; the Linux kernel's private <linux/statfs.h>
|
||||
* layout (signed longs, an int-pair f_fsid, a f_spare tail) never leaks
|
||||
* into it.
|
||||
*
|
||||
* Level 2 (muslmimic): the whole header (statvfs and fstatvfs are XSI
|
||||
* [CX] in POSIX.1-2008, not base; the base filesystem
|
||||
* queries are stat()/fstat() in <sys/stat.h>).
|
||||
*
|
||||
* f_flag holds ST_RDONLY and/or ST_NOSUID, and f_fsid is a best-effort
|
||||
* filesystem identifier (the kernel does not populate it for every
|
||||
* filesystem). The block counts are measured in f_frsize units (which is
|
||||
* f_bsize when the filesystem does not distinguish a transfer size).
|
||||
*
|
||||
* None of these declarations carries an intent attribute: both functions
|
||||
* perform I/O with side effects and report failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* fsblkcnt_t, fsfilcnt_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* Filesystem statistics. f_flag is a bitwise OR of ST_RDONLY and
|
||||
* ST_NOSUID; f_frsize is the fundamental block size the count fields are
|
||||
* expressed in. f_fsid is 0 when the filesystem provides no identifier.
|
||||
*/
|
||||
struct statvfs
|
||||
{
|
||||
unsigned long f_bsize; /* preferred I/O block size, bytes */
|
||||
unsigned long f_frsize; /* fundamental block size, bytes */
|
||||
fsblkcnt_t f_blocks; /* total blocks, in f_frsize units */
|
||||
fsblkcnt_t f_bfree; /* free blocks */
|
||||
fsblkcnt_t f_bavail; /* free blocks available to an unprivileged process */
|
||||
fsfilcnt_t f_files; /* total file serial numbers (inodes) */
|
||||
fsfilcnt_t f_ffree; /* free file serial numbers */
|
||||
fsfilcnt_t f_favail; /* free serial numbers available to an unprivileged process */
|
||||
unsigned long f_fsid; /* filesystem ID (best effort) */
|
||||
unsigned long f_flag; /* mount flags: ST_RDONLY | ST_NOSUID */
|
||||
unsigned long f_namemax; /* maximum file name length */
|
||||
};
|
||||
|
||||
/* f_flag values (also the kernel's f_flags bits on Linux). */
|
||||
#define ST_RDONLY 1 /* read-only filesystem */
|
||||
#define ST_NOSUID 2 /* set-user-ID/set-group-ID bits ignored on exec */
|
||||
|
||||
/*
|
||||
* Store statistics for the filesystem holding path into buf; return 0, or
|
||||
* -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
statvfs(const char *restrict path, struct statvfs *restrict buf);
|
||||
|
||||
/*
|
||||
* Like statvfs(), for the filesystem holding the open descriptor fildes;
|
||||
* return 0, or -1 with errno set. XSI.
|
||||
*/
|
||||
int
|
||||
fstatvfs(int fildes, struct statvfs *buf);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_STATVFS_H */
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef VLIBC_SYS_TIME_H
|
||||
#define VLIBC_SYS_TIME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/time.h>.
|
||||
*
|
||||
* Wall-clock access and per-process interval timers expressed in
|
||||
* microseconds. Everything here is XSI or BSD legacy — POSIX.1-2008 base
|
||||
* leaves only timerisset in this header — so the whole surface is gated at
|
||||
* level 2:
|
||||
*
|
||||
* Level 2 (muslmimic): gettimeofday, settimeofday, getitimer,
|
||||
* setitimer, utimes, futimes, lutimes,
|
||||
* struct timeval, struct itimerval,
|
||||
* struct timezone, the timer* macros.
|
||||
*
|
||||
* struct timeval is the same type <sys/select.h> uses for select()'s
|
||||
* timeout; that header defines it under the shared guard below so the two
|
||||
* headers agree and either may be included first. The layout is the kernel
|
||||
* ABI on x86_64 (two longs), which is why gettimeofday/setitimer pass it
|
||||
* through unmodified.
|
||||
*
|
||||
* ITIMER_REAL/ITIMER_VIRTUAL/ITIMER_PROF are kernel ABI values (the
|
||||
* getitimer/setitimer `which` argument). None of these declarations carries
|
||||
* an intent attribute: every function performs I/O with side effects and
|
||||
* reports failures through errno, so const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* time_t, suseconds_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI/BSD date-and-time interfaces. */
|
||||
|
||||
/* Interval-timer kinds (kernel ABI; the `which` argument). */
|
||||
#define ITIMER_REAL 0 /* count down in real time */
|
||||
#define ITIMER_VIRTUAL 1 /* count down in process virtual time */
|
||||
#define ITIMER_PROF 2 /* count down in process virtual + system time */
|
||||
|
||||
/*
|
||||
* Elapsed time in seconds and microseconds. Shared guard with
|
||||
* <sys/select.h>, which needs struct timeval for select()'s timeout at
|
||||
* level 1 and defines it under the same macro; either header may therefore
|
||||
* be included first. tv_usec holds 0..999999.
|
||||
*/
|
||||
#ifndef VLIBC_TIMEVAL_DEFINED
|
||||
#define VLIBC_TIMEVAL_DEFINED
|
||||
struct timeval
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
suseconds_t tv_usec; /* microseconds */
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An interval timer setting: it_value is the time to the next expiry,
|
||||
* it_interval the period between expiries after the first (both zero for a
|
||||
* one-shot or a disarmed timer). Kernel ABI layout (x86_64): two timevals.
|
||||
*/
|
||||
struct itimerval
|
||||
{
|
||||
struct timeval it_interval; /* interval between periodic expiries */
|
||||
struct timeval it_value; /* time to the next expiry */
|
||||
};
|
||||
|
||||
/*
|
||||
* Historic timezone record for settimeofday(). POSIX.1-2008 removed struct
|
||||
* timezone; it is kept for the legacy gettimeofday/settimeofday signatures,
|
||||
* whose tz argument is ignored (see src/time/gettimeofday.c).
|
||||
*/
|
||||
struct timezone
|
||||
{
|
||||
int tz_minuteswest; /* minutes west of Greenwich */
|
||||
int tz_dsttime; /* type of daylight-saving correction */
|
||||
};
|
||||
|
||||
/* True when the timer described by tp is armed. */
|
||||
#define timerisset(tp) ((tp)->tv_sec != 0 || (tp)->tv_usec != 0)
|
||||
|
||||
/* Disarm the timer described by tp (zero both fields). */
|
||||
#define timerclear(tp) ((tp)->tv_sec = (tp)->tv_usec = 0)
|
||||
|
||||
/*
|
||||
* Set *result to *a plus *b, normalizing the carry into tv_sec. The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
#define timeradd(a, b, result) \
|
||||
do \
|
||||
{ \
|
||||
(result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
|
||||
if ((result)->tv_usec >= 1000000) \
|
||||
{ \
|
||||
(result)->tv_sec++; \
|
||||
(result)->tv_usec -= 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Set *result to *a minus *b, normalizing the borrow out of tv_sec. The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
#define timersub(a, b, result) \
|
||||
do \
|
||||
{ \
|
||||
(result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
|
||||
(result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
|
||||
if ((result)->tv_usec < 0) \
|
||||
{ \
|
||||
(result)->tv_sec--; \
|
||||
(result)->tv_usec += 1000000; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* True when *a op *b, compared first by tv_sec and, on equality, by
|
||||
* tv_usec. op is a comparison operator (timercmp(x, y, <=)). The
|
||||
* arguments may be evaluated more than once (classic BSD form).
|
||||
*/
|
||||
/* clang-format off */
|
||||
#define timercmp(a, b, op) \
|
||||
(((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec op (b)->tv_usec) : ((a)->tv_sec op (b)->tv_sec))
|
||||
/* clang-format on */
|
||||
|
||||
/*
|
||||
* Read the current wall-clock time into tv (seconds since the Epoch and
|
||||
* microseconds 0..999999). tz is ignored for compatibility with the
|
||||
* historic two-argument form and may be anything, including NULL; tv may
|
||||
* also be NULL, in which case nothing is written. Return 0, or -1 with
|
||||
* errno set when tv points outside the address space.
|
||||
*/
|
||||
int
|
||||
gettimeofday(struct timeval *restrict tv, void *restrict tz);
|
||||
|
||||
/*
|
||||
* Set the kernel's idea of the current time from tv. tz, when not NULL, is
|
||||
* honored only together with tv; passing tz alone is unsupported (ENOTSUP).
|
||||
* Requires privilege. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
settimeofday(const struct timeval *tv, const struct timezone *tz);
|
||||
|
||||
/*
|
||||
* Store the current setting of interval timer which (ITIMER_REAL,
|
||||
* ITIMER_VIRTUAL or ITIMER_PROF) through value. Return 0, or -1 with errno
|
||||
* set for an unknown which or when value points outside the address space.
|
||||
*/
|
||||
int
|
||||
getitimer(int which, struct itimerval *value);
|
||||
|
||||
/*
|
||||
* Arm interval timer which from value (when value is not NULL) and store
|
||||
* the previous setting through ovalue (when not NULL). A value whose
|
||||
* it_value is zero disarms the timer. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
|
||||
|
||||
/*
|
||||
* Set the access (times[0]) and modification (times[1]) timestamps of path.
|
||||
* A NULL times array sets both to the current time. Return 0, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
int
|
||||
utimes(const char *path, const struct timeval times[2]);
|
||||
|
||||
/* utimes() on the open file fd. */
|
||||
int
|
||||
futimes(int fd, const struct timeval times[2]);
|
||||
|
||||
/* utimes() on the symbolic link path itself, never following it. */
|
||||
int
|
||||
lutimes(const char *path, const struct timeval times[2]);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_TIME_H */
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef VLIBC_SYS_TIMES_H
|
||||
#define VLIBC_SYS_TIMES_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/times.h>.
|
||||
*
|
||||
* Process CPU-time accounting in clock ticks. times() and struct tms are
|
||||
* XSI [CX] in POSIX.1-2008 (glibc gates them behind _XOPEN_SOURCE, not
|
||||
* _POSIX_C_SOURCE), so the whole surface sits at level 2 — nothing here is
|
||||
* POSIX.1-2008 base:
|
||||
*
|
||||
* Level 2 (muslmimic): times, struct tms.
|
||||
*
|
||||
* clock_t is the scalar tick type owned by <sys/types.h> (typedef long
|
||||
* clock_t; <time.h> pulls it from the same place for clock()); this header
|
||||
* includes that one rather than re-typedefing clock_t. The unit of every
|
||||
* times() figure is the tick: CLK_TCK per second, the fixed x86_64 USER_HZ
|
||||
* constant of 100 that todo 32's sysconf(_SC_CLK_TCK) reports (defined
|
||||
* once, in src/misc/sysconf.c).
|
||||
*
|
||||
* struct tms mirrors the x86_64 kernel's __kernel_tms exactly: four longs
|
||||
* in the same order (tms_utime, tms_stime, tms_cutime, tms_cstime), which
|
||||
* is why src/sys/times.c hands the struct straight to SYS_times. The kernel
|
||||
* writes all four words on every successful call, so no field is optional.
|
||||
*
|
||||
* The declaration carries no intent attribute: times() performs a syscall
|
||||
* with side effects and reports failures through errno, so const/pure
|
||||
* would be unsound (see the rationale in <sys/time.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h> /* clock_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI process-CPU-time accounting. */
|
||||
|
||||
/*
|
||||
* CPU-time accounting for the calling process and its waited-for children,
|
||||
* measured in clock ticks (CLK_TCK per second). tms_utime and tms_stime
|
||||
* are the user and system CPU time of the calling process; tms_cutime and
|
||||
* tms_cstime are the user and system CPU time consumed by children that
|
||||
* have been waited for (all four zeroed before any child is waited on).
|
||||
*/
|
||||
struct tms
|
||||
{
|
||||
clock_t tms_utime; /* user CPU time of the calling process */
|
||||
clock_t tms_stime; /* system CPU time of the calling process */
|
||||
clock_t tms_cutime; /* user CPU time of waited-for children */
|
||||
clock_t tms_cstime; /* system CPU time of waited-for children */
|
||||
};
|
||||
|
||||
/*
|
||||
* Store the process CPU-time accounting through buf and return the number
|
||||
* of clock ticks since an arbitrary fixed point in the past (the Linux
|
||||
* kernel reports ticks since boot). Return (clock_t)-1 with errno set when
|
||||
* buf points outside the address space.
|
||||
*/
|
||||
clock_t
|
||||
times(struct tms *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_TIMES_H */
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef VLIBC_SYS_TYPES_H
|
||||
#define VLIBC_SYS_TYPES_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/types.h>.
|
||||
*
|
||||
* The POSIX base scalar types. This public header is authoritative for
|
||||
* public consumers; src/internal/types.h mirrors the same x86_64 LP64
|
||||
* choices for internal use (both agree today — see the learnings note).
|
||||
* Widths follow the Linux x86_64 ABI, not the ISO minimums: long is 64-bit.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if VLIBC_HAS_HEADER_SYS_TYPES_H
|
||||
|
||||
/* Signed type for byte counts and read()/write() results (x86_64: long). */
|
||||
typedef __PTRDIFF_TYPE__ ssize_t;
|
||||
|
||||
/* Calendar time in seconds. */
|
||||
typedef long time_t;
|
||||
|
||||
/* Clock ticks (times(2)). */
|
||||
typedef long clock_t;
|
||||
|
||||
/* Clock id (clock_gettime(2)). */
|
||||
typedef int clockid_t;
|
||||
|
||||
/* Timer id (timer_create(2)). */
|
||||
typedef void *timer_t;
|
||||
|
||||
/* Process ID. */
|
||||
typedef int pid_t;
|
||||
|
||||
/* User and group IDs. */
|
||||
typedef unsigned int uid_t;
|
||||
typedef unsigned int gid_t;
|
||||
|
||||
/* General identifier. */
|
||||
typedef unsigned int id_t;
|
||||
|
||||
/* System V IPC key. */
|
||||
typedef int key_t;
|
||||
|
||||
/* File offset. */
|
||||
typedef long off_t;
|
||||
|
||||
/* File mode bits (and permissions). */
|
||||
typedef unsigned int mode_t;
|
||||
|
||||
/* Device and inode numbers. */
|
||||
typedef unsigned long long dev_t;
|
||||
typedef unsigned long long ino_t;
|
||||
|
||||
/* Hard link count. */
|
||||
typedef unsigned long long nlink_t;
|
||||
|
||||
/* Block counts and sizes. */
|
||||
typedef long blkcnt_t;
|
||||
typedef long blksize_t;
|
||||
|
||||
/* File-system block and file counts (statvfs). */
|
||||
typedef unsigned long fsblkcnt_t;
|
||||
typedef unsigned long fsfilcnt_t;
|
||||
|
||||
/* Microsecond intervals (timeval). */
|
||||
typedef long suseconds_t;
|
||||
typedef unsigned int useconds_t;
|
||||
|
||||
/* Socket address lengths. */
|
||||
typedef unsigned int socklen_t;
|
||||
|
||||
#endif /* VLIBC_HAS_HEADER_SYS_TYPES_H */
|
||||
|
||||
#endif /* VLIBC_SYS_TYPES_H */
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef VLIBC_SYS_UIO_H
|
||||
#define VLIBC_SYS_UIO_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/uio.h>.
|
||||
*
|
||||
* Scatter/gather I/O: readv() and writev() transfer data between a file
|
||||
* descriptor and a vector of memory buffers (POSIX.1-2008 base). Both are
|
||||
* unbuffered pass-throughs to the kernel: the iovec array is handed to
|
||||
* SYS_readv/SYS_writev as-is, and the kernel applies its own validation
|
||||
* (iovcnt in 0..IOV_MAX, iov NULL with iovcnt > 0 faults as EFAULT). Failures
|
||||
* are reported as -1 with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): readv, writev.
|
||||
*
|
||||
* The Linux extensions that build on this ABI (preadv/pwritev,
|
||||
* process_vm_readv/process_vm_writev, ...) are deliberately absent — they are
|
||||
* not POSIX and belong to a later compatibility profile.
|
||||
*
|
||||
* struct iovec matches the kernel's struct iovec (x86_64: a pointer and a
|
||||
* size_t, no padding). IOV_MAX is 1024, the Linux UIO_MAXIOV; the kernel
|
||||
* rejects any iovcnt above it with EINVAL.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Maximum number of iovec entries a single readv()/writev() call accepts. */
|
||||
#define IOV_MAX 1024
|
||||
|
||||
/*
|
||||
* A scatter/gather segment: iov_base points at the buffer (or, for readv,
|
||||
* the buffer to fill) and iov_len is its size in bytes. Kernel layout, as
|
||||
* above.
|
||||
*/
|
||||
struct iovec
|
||||
{
|
||||
void *iov_base; /* starting address of the segment */
|
||||
size_t iov_len; /* number of bytes in the segment */
|
||||
};
|
||||
|
||||
/*
|
||||
* Read up to the sum of iov_len bytes from fd into the iovcnt buffers of
|
||||
* iov, in order; return the number of bytes read, 0 at end of file, or -1
|
||||
* with errno set on error. Buffers are filled completely before the next
|
||||
* one is touched.
|
||||
*/
|
||||
ssize_t
|
||||
readv(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
||||
/*
|
||||
* Write up to the sum of iov_len bytes from the iovcnt buffers of iov to
|
||||
* fd, in order; return the number of bytes written, or -1 with errno set
|
||||
* on error. The kernel applies its own validation (iovcnt 0..IOV_MAX, iov
|
||||
* NULL with iovcnt > 0 faults as EFAULT).
|
||||
*/
|
||||
ssize_t
|
||||
writev(int fd, const struct iovec *iov, int iovcnt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UIO_H */
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef VLIBC_SYS_UTSNAME_H
|
||||
#define VLIBC_SYS_UTSNAME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/utsname.h>.
|
||||
*
|
||||
* System identification: uname() and struct utsname. uname is XSI [CX] in
|
||||
* POSIX.1-2008 (glibc gates it behind _XOPEN_SOURCE, not _POSIX_C_SOURCE),
|
||||
* so the whole surface sits at level 2 — nothing here is POSIX.1-2008 base.
|
||||
*
|
||||
* Level 2 (muslmimic): uname, struct utsname.
|
||||
*
|
||||
* struct utsname mirrors the x86_64 kernel's struct new_utsname exactly: six
|
||||
* NUL-terminated 65-byte character arrays (the kernel's _UTSNAME_LENGTH, 64
|
||||
* usable characters plus the terminator). The kernel's uname syscall copies
|
||||
* all 390 bytes, so the layout is an ABI fact, not a style choice.
|
||||
* domainname is a Linux extension (the kernel's uts namespace domain name)
|
||||
* and is not part of the POSIX struct; it is included for glibc
|
||||
* compatibility — POSIX only requires the first five fields.
|
||||
*
|
||||
* The declaration carries no intent attribute: uname performs I/O with side
|
||||
* effects and reports failures through errno, so const/pure would be
|
||||
* unsound (see the rationale in <unistd.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* The system identification strings. Each field is NUL-terminated; the
|
||||
* 65-byte width matches the kernel buffer (64 characters plus the NUL).
|
||||
* sysname is the operating system name ("Linux"), nodename the host name,
|
||||
* release the kernel release, version the kernel build, machine the
|
||||
* hardware identifier ("x86_64"), and domainname the NIS/UTS domain name.
|
||||
*/
|
||||
struct utsname
|
||||
{
|
||||
char sysname[65]; /* operating system name */
|
||||
char nodename[65]; /* name within the communications network */
|
||||
char release[65]; /* operating system release level */
|
||||
char version[65]; /* operating system version */
|
||||
char machine[65]; /* hardware type */
|
||||
char domainname[65]; /* NIS/UTS domain name (Linux extension) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Fill the buffer with the current system identification strings; return
|
||||
* 0, or -1 with errno set when the buffer points outside the address
|
||||
* space. The strings are copies from the kernel's UTS namespace.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UTSNAME_H */
|
||||
@@ -0,0 +1,207 @@
|
||||
#ifndef VLIBC_SYS_WAIT_H
|
||||
#define VLIBC_SYS_WAIT_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/wait.h>.
|
||||
*
|
||||
* Child-process status collection (POSIX.1-2008) and the wait status macros.
|
||||
* Everything here is a pass-through to the kernel: wait/waitpid/wait3/wait4
|
||||
* ride SYS_wait4, waitid rides SYS_waitid, and failures are reported as -1
|
||||
* (or the child pid on success) with errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): wait, waitpid, waitid + the W* status macros.
|
||||
* Level 2 (muslmimic): wait3, wait4 (XSI).
|
||||
*
|
||||
* The W* decode macros follow the kernel's wait status encoding: bits 0-6
|
||||
* hold the terminating signal (or 0x7f for a stop), bit 7 the core-dump
|
||||
* flag, bits 8-15 the exit status, and the word 0xffff marks a continued
|
||||
* child. WEXITED/WSTOPPED/WNOWAIT are the waitid-only option bits (XSI;
|
||||
* kernel-identical values) and WCOREDUMP is an XSI/Linux extension (bit
|
||||
* 0x80) kept alongside the POSIX set for source compatibility.
|
||||
*
|
||||
* None of the declarations carry an intent attribute: every function has
|
||||
* kernel-visible side effects and reports failures through errno.
|
||||
*
|
||||
* <signal.h> does not exist yet (a later todo owns it). The minimal
|
||||
* siginfo_t below and the CLD_* constants live here under guards so that
|
||||
* header can take them over without conflict; see the notes by each.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Selector type for waitid(). POSIX defines idtype_t as an integer type and
|
||||
* places it in <sys/types.h>, which does not define it yet; it is provided
|
||||
* here under a VLIBC_ guard so sys/types.h can take it over later without a
|
||||
* redefinition. int matches the kernel's `which` argument on x86_64.
|
||||
*/
|
||||
#ifndef VLIBC_DEFINED_IDTYPE_T
|
||||
#define VLIBC_DEFINED_IDTYPE_T
|
||||
typedef int idtype_t;
|
||||
#endif
|
||||
|
||||
/* waitid() idtype selectors (kernel-identical). */
|
||||
#define P_ALL 0 /* wait for any child */
|
||||
#define P_PID 1 /* wait for the specific child */
|
||||
#define P_PGID 2 /* wait for any child in the process group */
|
||||
|
||||
/* wait/waitpid option bits (kernel-identical). */
|
||||
#define WNOHANG 1 /* do not block; return 0 if no child has exited */
|
||||
#define WUNTRACED 2 /* also report stopped children */
|
||||
#define WCONTINUED 8 /* also report continued children */
|
||||
|
||||
/*
|
||||
* waitid-only option bits (kernel-identical; XSI). WSTOPPED shares its
|
||||
* value with WUNTRACED, so the two names are interchangeable where both
|
||||
* apply.
|
||||
*/
|
||||
#define WEXITED 4 /* wait for exited children */
|
||||
#define WSTOPPED 2 /* wait for stopped children */
|
||||
#define WNOWAIT 0x01000000 /* report but do not reap */
|
||||
|
||||
/* Status decode macros. The argument is the raw wait status word. */
|
||||
|
||||
/* True when the child terminated normally via exit() or _exit(). */
|
||||
#define WIFEXITED(s) (((s) & 0x7f) == 0)
|
||||
|
||||
/* Exit status of a normally terminated child (WIFEXITED true). */
|
||||
#define WEXITSTATUS(s) (((s) & 0xff00) >> 8)
|
||||
|
||||
/*
|
||||
* True when the child was killed by a signal. The 0x7f exclusion keeps the
|
||||
* stop code (0x7f) from being misread as a terminating signal.
|
||||
*/
|
||||
#define WIFSIGNALED(s) (((s) & 0x7f) != 0 && ((s) & 0x7f) != 0x7f)
|
||||
|
||||
/* Number of the signal that killed the child (WIFSIGNALED true). */
|
||||
#define WTERMSIG(s) ((s) & 0x7f)
|
||||
|
||||
/* True when the child is stopped by a signal (WUNTRACED). */
|
||||
#define WIFSTOPPED(s) (((s) & 0xff) == 0x7f)
|
||||
|
||||
/* Number of the signal that stopped the child (WIFSTOPPED true). */
|
||||
#define WSTOPSIG(s) WEXITSTATUS(s)
|
||||
|
||||
/* True when the child was resumed by SIGCONT (WCONTINUED). */
|
||||
#define WIFCONTINUED(s) ((s) == 0xffff)
|
||||
|
||||
/* True when the killed child dumped core (XSI/Linux extension, bit 0x80). */
|
||||
#define WCOREDUMP(s) (((s) & 0x80) != 0)
|
||||
|
||||
/*
|
||||
* si_code values reported by waitid() (kernel-identical). POSIX defines
|
||||
* these in <signal.h>, which does not exist yet; guarded per name so the
|
||||
* future signal.h can define them without a redefinition warning.
|
||||
*/
|
||||
#ifndef CLD_EXITED
|
||||
#define CLD_EXITED 1 /* child exited normally */
|
||||
#define CLD_KILLED 2 /* child killed by a signal */
|
||||
#define CLD_DUMPED 3 /* child killed by a signal and dumped core */
|
||||
#define CLD_TRAPPED 4 /* child stopped by a trace event */
|
||||
#define CLD_STOPPED 5 /* child stopped by a signal */
|
||||
#define CLD_CONTINUED 6 /* child resumed by SIGCONT */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Minimal siginfo_t, sized and laid out to match the kernel's x86_64
|
||||
* siginfo_t (128 bytes) for the fields waitid() fills: si_signo, si_errno,
|
||||
* si_code at offsets 0/4/8, then the wait-fields si_pid/si_uid/si_status at
|
||||
* offsets 16/20/24. The union is 8-aligned (si_utime/si_stime are 8-byte
|
||||
* clock_t in both the kernel and glibc layouts), so it starts at offset 16
|
||||
* with implicit padding after si_code. The kernel copies the full 128 bytes,
|
||||
* so the size must stay 128; the pad member guarantees it and the static
|
||||
* asserts pin the layout. The signal-handling fields (si_addr, si_value,
|
||||
* timers, ...) are deliberately absent — the future <signal.h> owns the
|
||||
* complete siginfo_t and must reconcile this guard.
|
||||
*/
|
||||
#ifndef VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
#define VLIBC_INTERNAL_SIGINFO_DEFINED
|
||||
typedef struct
|
||||
{
|
||||
int si_signo;
|
||||
int si_errno;
|
||||
int si_code;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
pid_t si_pid; /* 16 */
|
||||
uid_t si_uid; /* 20 */
|
||||
int si_status; /* 24 */
|
||||
long si_utime; /* 32 — 8-byte clock_t, matches the kernel/glibc ABI */
|
||||
long si_stime; /* 40 */
|
||||
};
|
||||
int vlibc_siginfo_pad[28]; /* union sized 112 so the struct stays 128 */
|
||||
};
|
||||
} siginfo_t;
|
||||
|
||||
_Static_assert(sizeof(siginfo_t) == 128, "siginfo_t must match the kernel size");
|
||||
_Static_assert(offsetof(siginfo_t, si_pid) == 16, "si_pid must sit at offset 16");
|
||||
#endif
|
||||
|
||||
/* Level 1 (POSIX base). */
|
||||
|
||||
/*
|
||||
* Wait for any child to terminate or stop and store its status in
|
||||
* *stat_loc (NULL skips the store); return the child pid, or -1 with
|
||||
* errno set. Equivalent to waitpid(-1, stat_loc, 0).
|
||||
*/
|
||||
pid_t
|
||||
wait(int *stat_loc);
|
||||
|
||||
/*
|
||||
* Wait for the child identified by pid (-1: any child, 0: any child in the
|
||||
* calling process group, < -1: any child in the process group -pid) and
|
||||
* store its status in *stat_loc (NULL skips the store). options are the
|
||||
* WNOHANG/WUNTRACED/WCONTINUED bits. Return the child pid, 0 when WNOHANG
|
||||
* found nothing, or -1 with errno set.
|
||||
*/
|
||||
pid_t
|
||||
waitpid(pid_t pid, int *stat_loc, int options);
|
||||
|
||||
/*
|
||||
* Wait for a child selected by idtype/id (P_ALL, P_PID, P_PGID) and fill
|
||||
* *infop with the siginfo details (si_pid, si_uid, si_status and a CLD_*
|
||||
* si_code); options are the WEXITED/WSTOPPED/WCONTINUED/WNOHANG/WNOWAIT
|
||||
* bits. Return 0, or -1 with errno set. infop must point to at least
|
||||
* 128 bytes (the kernel writes a full siginfo).
|
||||
*/
|
||||
int
|
||||
waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): XSI. */
|
||||
|
||||
/*
|
||||
* struct rusage is defined by <sys/resource.h>, which does not exist yet;
|
||||
* a forward declaration is enough to pass a pointer through to the kernel.
|
||||
*/
|
||||
struct rusage;
|
||||
|
||||
/*
|
||||
* Like waitpid(-1, stat_loc, options), and additionally store resource
|
||||
* usage in *rusage (NULL skips the store); return the child pid, or -1
|
||||
* with errno set. XSI.
|
||||
*/
|
||||
pid_t
|
||||
wait3(int *stat_loc, int options, struct rusage *rusage);
|
||||
|
||||
/*
|
||||
* Like waitpid(), and additionally store resource usage in *rusage (NULL
|
||||
* skips the store); return the child pid, or -1 with errno set. XSI.
|
||||
*/
|
||||
pid_t
|
||||
wait4(pid_t pid, int *stat_loc, int options, struct rusage *rusage);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_WAIT_H */
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifndef VLIBC_SYSLOG_H
|
||||
#define VLIBC_SYSLOG_H
|
||||
|
||||
/*
|
||||
* vlibc — <syslog.h>.
|
||||
*
|
||||
* The syslog(3) logging interface, writing BSD-syslog datagrams to the
|
||||
* /dev/log socket (see src/misc/syslog.c for the wire details). syslog is
|
||||
* NOT POSIX.1-2008 base — it is XSI [CX] (and historical BSD), so this
|
||||
* ENTIRE header is gated at `VLIBC_LEVEL >= 2`: at level 1 (onlyposix) it is
|
||||
* empty, exactly like <strings.h>.
|
||||
*
|
||||
* The interface has three parts:
|
||||
*
|
||||
* - facilities and severities, plus the option flags of openlog. The
|
||||
* facility constants carry the classic BSD values, which are the Linux
|
||||
* wire ABI for /dev/log: facility = code << 3, severity = 0..7, and the
|
||||
* datagram's numeric priority is facility | severity. The facility code
|
||||
* is passed on the wire, so these numbers must match what syslogd
|
||||
* expects and are therefore kernel-ABI facts, not invented values;
|
||||
*
|
||||
* - LOG_MASK / LOG_UPTO, the two macros that build the mask argument of
|
||||
* setlogmask (LOG_MASK is POSIX; LOG_UPTO is the classic convenience
|
||||
* form both musl and glibc provide);
|
||||
*
|
||||
* - openlog / syslog / closelog / setlogmask and the vsyslog entry point
|
||||
* the standard requires syslog to be implemented in terms of. None of
|
||||
* them carries an intent attribute: every one reads and writes global
|
||||
* logging state and performs I/O, so const/pure would be unsound.
|
||||
*
|
||||
* %m handling: inside the syslog format string, "%m" is replaced by the
|
||||
* strerror(errno) text (a syslog-specific feature, implemented in syslog.c —
|
||||
* NOT a printf conversion). All other conversions are the standard ones,
|
||||
* handled by the library's own vsnprintf.
|
||||
*
|
||||
* This header includes <vlibc/features.h> itself, so the gate below always
|
||||
* sees the configured VLIBC_LEVEL even when the caller included no vlibc
|
||||
* header first, and <stdarg.h> for va_list.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- openlog option flags (bit mask) ---- */
|
||||
|
||||
#define LOG_PID 0x01 /* include the calling process' PID in each message */
|
||||
#define LOG_CONS 0x02 /* on send failure, write the message to the console */
|
||||
#define LOG_ODELAY 0x04 /* delay opening the /dev/log socket until first use */
|
||||
#define LOG_NDELAY 0x08 /* open the /dev/log socket immediately in openlog */
|
||||
#define LOG_NOWAIT 0x10 /* do not wait for child processes (accepted, unused) */
|
||||
#define LOG_PERROR 0x20 /* also write each message to standard error */
|
||||
|
||||
/* ---- facility codes (code << 3; the wire value of the datagram) ---- */
|
||||
|
||||
#define LOG_KERN 0 /* kernel messages */
|
||||
#define LOG_USER (1 << 3) /* random user-level messages */
|
||||
#define LOG_MAIL (2 << 3) /* mail system */
|
||||
#define LOG_DAEMON (3 << 3) /* system daemons */
|
||||
#define LOG_AUTH (4 << 3) /* security/authorization messages */
|
||||
#define LOG_LPR (6 << 3) /* line printer subsystem */
|
||||
#define LOG_NEWS (7 << 3) /* network news subsystem */
|
||||
#define LOG_UUCP (8 << 3) /* UUCP subsystem */
|
||||
#define LOG_CRON (9 << 3) /* clock daemon */
|
||||
#define LOG_LOCAL0 (16 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL1 (17 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL2 (18 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL3 (19 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL4 (20 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL5 (21 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL6 (22 << 3) /* reserved for local use */
|
||||
#define LOG_LOCAL7 (23 << 3) /* reserved for local use */
|
||||
|
||||
/* ---- severities (the low three bits of the priority value) ---- */
|
||||
|
||||
#define LOG_EMERG 0 /* system is unusable */
|
||||
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||
#define LOG_CRIT 2 /* critical conditions */
|
||||
#define LOG_ERR 3 /* error conditions */
|
||||
#define LOG_WARNING 4 /* warning conditions */
|
||||
#define LOG_NOTICE 5 /* normal but significant condition */
|
||||
#define LOG_INFO 6 /* informational */
|
||||
#define LOG_DEBUG 7 /* debug-level messages */
|
||||
|
||||
/* Bit field holding the facility part (bits 3..9) of a priority value. */
|
||||
#define LOG_FACMASK 0x03f8
|
||||
|
||||
/* Bit field holding the severity part (bits 0..2) of a priority value. */
|
||||
#define LOG_PRIMASK 0x07
|
||||
|
||||
/* A mask with exactly pri's bit set (pri is a LOG_* severity, 0..7). */
|
||||
#define LOG_MASK(pri) (1 << (pri))
|
||||
|
||||
/* A mask with every severity from LOG_EMERG through pri included. */
|
||||
#define LOG_UPTO(pri) ((1 << ((pri) + 1)) - 1)
|
||||
|
||||
/*
|
||||
* Open a connection to the system logger. ident, when non-NULL, is copied
|
||||
* (a caller-owned pointer is not retained) and prefixes every message; a
|
||||
* NULL ident logs messages with no tag. option ORs in the LOG_* option
|
||||
* flags; facility is one of the LOG_* facility constants and is used for
|
||||
* messages whose priority carries no facility of its own. With LOG_NDELAY
|
||||
* the /dev/log socket is opened here; otherwise (and by default) it is
|
||||
* opened lazily on the first syslog call, and a failure to open it is
|
||||
* silent. Prior state — the mask from setlogmask and the default facility
|
||||
* LOG_USER — persists.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
openlog(const char *ident, int option, int facility);
|
||||
|
||||
/*
|
||||
* Log a message: build "<facility|severity>ident[pid]: message" and send it
|
||||
* as one datagram to /dev/log, subject to the setlogmask mask. severity is
|
||||
* the low three bits of priority; a facility encoded in priority overrides
|
||||
* the openlog facility, and an out-of-range (high-bit) facility is ignored,
|
||||
* falling back to the openlog facility. The format string is the printf
|
||||
* family's, with "%m" additionally meaning the strerror(errno) text. When
|
||||
* LOG_PERROR was passed to openlog the composed line also goes to standard
|
||||
* error; when the send fails and LOG_CONS was passed, it goes to the console
|
||||
* instead. A failed open or send never terminates the process.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
syslog(int priority, const char *format, ...);
|
||||
|
||||
/*
|
||||
* Close the connection to the system logger: close the /dev/log socket if
|
||||
* one is open, and clear the ident and option state installed by openlog
|
||||
* (the facility and mask are retained). A later syslog reopens the socket
|
||||
* lazily.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
closelog(void);
|
||||
|
||||
/*
|
||||
* The engine behind syslog: identical semantics, taking a va_list instead
|
||||
* of the variadic tail. Each message is formatted exactly once (through
|
||||
* vsnprintf into an internal buffer) before any send, so LOG_PERROR and the
|
||||
* datagram always carry the same text.
|
||||
*
|
||||
* I/O and global state: no intent attribute.
|
||||
*/
|
||||
void
|
||||
vsyslog(int priority, const char *format, va_list ap);
|
||||
|
||||
/*
|
||||
* Set the process' log priority mask to maskpri — a bit per severity where
|
||||
* bit LOG_MASK(severity) set means "log this severity" — and return the
|
||||
* previous mask. A mask of 0 suppresses every message. The mask applies to
|
||||
* the severity only: facility is never filtered.
|
||||
*/
|
||||
int
|
||||
setlogmask(int maskpri);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#endif /* VLIBC_SYSLOG_H */
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef VLIBC_TAR_H
|
||||
#define VLIBC_TAR_H
|
||||
|
||||
/*
|
||||
* vlibc — <tar.h>.
|
||||
*
|
||||
* Constants for the ustar tar archive format (POSIX). Constants only: the
|
||||
* header magic and version strings, the typeflag characters that identify
|
||||
* entry kinds, and the mode bits stored in each header.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_HAS_HEADER_TAR_H
|
||||
|
||||
/* Header magic (6 bytes) and version (2 bytes). */
|
||||
#define TMAGIC "ustar"
|
||||
#define TMAGLEN 6
|
||||
#define TVERSION "00"
|
||||
#define TVERSLEN 2
|
||||
|
||||
/* Typeflag characters naming the entry kind. */
|
||||
#define REGTYPE '0'
|
||||
#define AREGTYPE '\0'
|
||||
#define LNKTYPE '1'
|
||||
#define SYMTYPE '2'
|
||||
#define CHRTYPE '3'
|
||||
#define BLKTYPE '4'
|
||||
#define DIRTYPE '5'
|
||||
#define FIFOTYPE '6'
|
||||
#define CONTTYPE '7'
|
||||
|
||||
/* Set-id and sticky bits. */
|
||||
#define TSUID 04000
|
||||
#define TSGID 02000
|
||||
#define TSVTX 01000
|
||||
|
||||
/* Mode bits (permissions): user, group, other. */
|
||||
#define TUREAD 00400
|
||||
#define TUWRITE 00200
|
||||
#define TUEXEC 00100
|
||||
#define TGREAD 00040
|
||||
#define TGWRITE 00020
|
||||
#define TGEXEC 00010
|
||||
#define TOREAD 00004
|
||||
#define TOWRITE 00002
|
||||
#define TOEXEC 00001
|
||||
|
||||
#endif /* VLIBC_HAS_HEADER_TAR_H */
|
||||
|
||||
#endif /* VLIBC_TAR_H */
|
||||
@@ -0,0 +1,366 @@
|
||||
#ifndef VLIBC_TERMIOS_H
|
||||
#define VLIBC_TERMIOS_H
|
||||
|
||||
/*
|
||||
* vlibc — <termios.h>.
|
||||
*
|
||||
* General terminal interface (POSIX.1-2008 base): the termios structure and
|
||||
* the tcgetattr/tcsetattr/tcsendbreak/tcdrain/tcflush/tcflow query-and-set
|
||||
* functions plus the cfgetispeed/cfgetospeed/cfsetispeed/cfsetospeed baud
|
||||
* helpers, over the kernel's TCGETS/TCSETS ioctls (SYS_ioctl).
|
||||
*
|
||||
* Level 1 (onlyposix): tcgetattr, tcsetattr, tcsendbreak, tcdrain,
|
||||
* tcflush, tcflow, cfgetispeed, cfgetospeed,
|
||||
* cfsetispeed, cfsetospeed (POSIX base).
|
||||
* Level 2 (muslmimic): cfmakeraw, cfsetspeed (BSD/glibc extensions),
|
||||
* tcgetsid (XSI [CX] — glibc declares it only under
|
||||
* __USE_XOPEN2K8, not base __USE_POSIX).
|
||||
*
|
||||
* struct termios is a byte-for-byte copy of the x86_64 kernel termios
|
||||
* (asm-generic/termbits.h), pinned by the static assertions below. The
|
||||
* kernel TCGETS/TCSETS ioctls copy exactly sizeof(struct termios) bytes of
|
||||
* this layout (verified empirically on this host: TCGETS writes 36 bytes);
|
||||
* any divergence would silently corrupt c_cc. Unlike glibc's 60-byte
|
||||
* userspace struct (NCCS 32 + trailing ispeed/ospeed members), the speed
|
||||
* is carried only in the c_cflag CBAUD/CIBAUD bit fields, exactly as the
|
||||
* kernel encodes it — so the ioctl copies the whole structure both ways
|
||||
* with nothing left stale.
|
||||
*
|
||||
* The baud-rate constants B0..B4000000 are the KERNEL CBAUD encodings
|
||||
* (B9600 is 0x0d, B38400 is 0x0f, B57600 is 0x1001, ...), NOT glibc's
|
||||
* real-numbers-in-speed_t scheme. cfgetospeed()/cfgetispeed() return these
|
||||
* constants and cfsetospeed()/cfsetispeed()/cfsetspeed() accept them, so
|
||||
* a speed round-trips through the c_cflag bits untouched. This is the same
|
||||
* choice musl makes.
|
||||
*
|
||||
* All flag, control-character, baud, and action constants below are Linux
|
||||
* x86_64 kernel-UAPI facts transcribed from asm-generic/termbits.h and
|
||||
* asm-generic/termbits-common.h. The ioctl request numbers (TCGETS,
|
||||
* TCSETS, TCSBRK, TCXONC, TCFLSH, TIOCGSID) are Linux-only and stay
|
||||
* private to src/termios/termios.c, matching the sys/ioctl.h-less layout
|
||||
* of the tree today (they belong to a future <sys/ioctl.h>).
|
||||
*
|
||||
* No declaration carries an intent attribute: tcgetattr/tcsetattr &c
|
||||
* perform I/O, and the cf* speed helpers read or write the caller's
|
||||
* termios through a pointer that may alias arbitrary memory — const/pure
|
||||
* would be unsound (same rationale as <fcntl.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Basic types ---- */
|
||||
|
||||
typedef unsigned char cc_t; /* control-character value */
|
||||
typedef unsigned int speed_t; /* baud rate (a B* constant) */
|
||||
typedef unsigned int tcflag_t; /* mode-flag bit mask */
|
||||
|
||||
/* ---- struct termios (x86_64 kernel layout) ---- */
|
||||
|
||||
#define NCCS 19 /* control-character slots (kernel UAPI) */
|
||||
|
||||
struct termios
|
||||
{
|
||||
tcflag_t c_iflag; /* 0: input mode flags */
|
||||
tcflag_t c_oflag; /* 4: output mode flags */
|
||||
tcflag_t c_cflag; /* 8: control mode flags */
|
||||
tcflag_t c_lflag; /* 12: local mode flags */
|
||||
cc_t c_line; /* 16: line discipline */
|
||||
cc_t c_cc[NCCS]; /* 17: control characters */
|
||||
};
|
||||
|
||||
/* Pin the kernel layout (TCGETS/TCSETS copy this exact size and shape). */
|
||||
_Static_assert(sizeof(struct termios) == 36, "struct termios must match the x86_64 kernel layout");
|
||||
_Static_assert(offsetof(struct termios, c_lflag) == 12, "c_lflag must sit at offset 12");
|
||||
_Static_assert(offsetof(struct termios, c_line) == 16, "c_line must sit at offset 16");
|
||||
_Static_assert(offsetof(struct termios, c_cc) == 17, "c_cc must sit at offset 17");
|
||||
|
||||
/* ---- Control characters (indices into c_cc) ---- */
|
||||
|
||||
#define VINTR 0 /* interrupt character */
|
||||
#define VQUIT 1 /* quit character */
|
||||
#define VERASE 2 /* erase character */
|
||||
#define VKILL 3 /* kill character */
|
||||
#define VEOF 4 /* end-of-file character */
|
||||
#define VTIME 5 /* timeout for non-canonical read */
|
||||
#define VMIN 6 /* min characters for non-canonical read */
|
||||
#define VSWTC 7 /* switch character (System V) */
|
||||
#define VSTART 8 /* start character */
|
||||
#define VSTOP 9 /* stop character */
|
||||
#define VSUSP 10 /* suspend character */
|
||||
#define VEOL 11 /* alternate end-of-line character */
|
||||
#define VREPRINT 12 /* reprint character */
|
||||
#define VDISCARD 13 /* discard character */
|
||||
#define VWERASE 14 /* word-erase character */
|
||||
#define VLNEXT 15 /* literal-next character */
|
||||
#define VEOL2 16 /* alternate end-of-line character */
|
||||
|
||||
/* ---- c_iflag: input mode flags ---- */
|
||||
|
||||
#define IGNBRK 0x0001 /* ignore break condition */
|
||||
#define BRKINT 0x0002 /* signal interrupt on break */
|
||||
#define IGNPAR 0x0004 /* ignore characters with parity errors */
|
||||
#define PARMRK 0x0008 /* mark parity and framing errors */
|
||||
#define INPCK 0x0010 /* enable input parity check */
|
||||
#define ISTRIP 0x0020 /* strip 8th bit off characters */
|
||||
#define INLCR 0x0040 /* map NL to CR on input */
|
||||
#define IGNCR 0x0080 /* ignore CR */
|
||||
#define ICRNL 0x0100 /* map CR to NL on input */
|
||||
#define IUCLC 0x0200 /* map uppercase to lowercase on input */
|
||||
#define IXON 0x0400 /* enable start/stop output control */
|
||||
#define IXANY 0x0800 /* any character will restart after stop */
|
||||
#define IXOFF 0x1000 /* enable start/stop input control */
|
||||
#define IMAXBEL 0x2000 /* ring bell on input queue full */
|
||||
#define IUTF8 0x4000 /* input is UTF-8 */
|
||||
|
||||
/* ---- c_oflag: output mode flags ---- */
|
||||
|
||||
#define OPOST 0x0001 /* perform output processing */
|
||||
#define OLCUC 0x0002 /* map lowercase to uppercase on output */
|
||||
#define ONLCR 0x0004 /* map NL to CR-NL on output */
|
||||
#define OCRNL 0x0008 /* map CR to NL on output */
|
||||
#define ONOCR 0x0010 /* no CR output at column 0 */
|
||||
#define ONLRET 0x0020 /* NL performs CR function on output */
|
||||
#define OFILL 0x0040 /* use fill characters for delay */
|
||||
#define OFDEL 0x0080 /* fill is DEL, else NUL */
|
||||
#define NLDLY 0x0100 /* newline delay mask */
|
||||
#define NL0 0x0000
|
||||
#define NL1 0x0100
|
||||
#define CRDLY 0x0600 /* carriage-return delay mask */
|
||||
#define CR0 0x0000
|
||||
#define CR1 0x0200
|
||||
#define CR2 0x0400
|
||||
#define CR3 0x0600
|
||||
#define TABDLY 0x1800 /* horizontal-tab delay mask */
|
||||
#define TAB0 0x0000
|
||||
#define TAB1 0x0800
|
||||
#define TAB2 0x1000
|
||||
#define TAB3 0x1800
|
||||
#define XTABS 0x1800 /* TAB3: expand tabs to spaces */
|
||||
#define BSDLY 0x2000 /* backspace delay mask */
|
||||
#define BS0 0x0000
|
||||
#define BS1 0x2000
|
||||
#define VTDLY 0x4000 /* vertical-tab delay mask */
|
||||
#define VT0 0x0000
|
||||
#define VT1 0x4000
|
||||
#define FFDLY 0x8000 /* form-feed delay mask */
|
||||
#define FF0 0x0000
|
||||
#define FF1 0x8000
|
||||
|
||||
/* ---- c_cflag: control mode flags ---- */
|
||||
|
||||
#define CBAUD 0x100f /* baud-rate mask (includes CBAUDEX) */
|
||||
#define CSIZE 0x0030 /* character-size mask */
|
||||
#define CS5 0x0000 /* 5 bits */
|
||||
#define CS6 0x0010 /* 6 bits */
|
||||
#define CS7 0x0020 /* 7 bits */
|
||||
#define CS8 0x0030 /* 8 bits */
|
||||
#define CSTOPB 0x0040 /* two stop bits, else one */
|
||||
#define CREAD 0x0080 /* enable receiver */
|
||||
#define PARENB 0x0100 /* enable parity */
|
||||
#define PARODD 0x0200 /* odd parity, else even */
|
||||
#define HUPCL 0x0400 /* hang up on last close */
|
||||
#define CLOCAL 0x0800 /* ignore modem status lines */
|
||||
#define CBAUDEX 0x1000 /* extended baud-rate mask (above B38400) */
|
||||
#define CRTSCTS 0x80000000 /* enable RTS/CTS flow control */
|
||||
|
||||
/* ---- c_lflag: local mode flags ---- */
|
||||
|
||||
#define ISIG 0x0001 /* enable signals */
|
||||
#define ICANON 0x0002 /* canonical mode (line-buffered) */
|
||||
#define XCASE 0x0004 /* canonical uppercase/lowercase presentation */
|
||||
#define ECHO 0x0008 /* enable echo */
|
||||
#define ECHOE 0x0010 /* echo erase as backspace */
|
||||
#define ECHOK 0x0020 /* echo KILL */
|
||||
#define ECHONL 0x0040 /* echo NL */
|
||||
#define NOFLSH 0x0080 /* disable flush after interrupt/quit/suspend */
|
||||
#define TOSTOP 0x0100 /* send SIGTTOU on background output */
|
||||
#define ECHOCTL 0x0200 /* echo control characters as ^X */
|
||||
#define ECHOPRT 0x0400 /* echo erase as backspace-print-backspace */
|
||||
#define ECHOKE 0x0800 /* BSD: erase entire line on KILL */
|
||||
#define FLUSHO 0x1000 /* output is being flushed */
|
||||
#define PENDIN 0x4000 /* reprint pending input at next read */
|
||||
#define IEXTEN 0x8000 /* enable implementation-defined input processing */
|
||||
#define EXTPROC 0x10000 /* external processing */
|
||||
|
||||
/* ---- Baud rates (kernel CBAUD encodings — see the header comment) ---- */
|
||||
|
||||
#define B0 0x00000000 /* hang up */
|
||||
#define B50 0x00000001
|
||||
#define B75 0x00000002
|
||||
#define B110 0x00000003
|
||||
#define B134 0x00000004
|
||||
#define B150 0x00000005
|
||||
#define B200 0x00000006
|
||||
#define B300 0x00000007
|
||||
#define B600 0x00000008
|
||||
#define B1200 0x00000009
|
||||
#define B1800 0x0000000a
|
||||
#define B2400 0x0000000b
|
||||
#define B4800 0x0000000c
|
||||
#define B9600 0x0000000d
|
||||
#define B19200 0x0000000e
|
||||
#define B38400 0x0000000f
|
||||
#define B57600 0x00001001 /* CBAUDEX | 1 */
|
||||
#define B115200 0x00001002
|
||||
#define B230400 0x00001003
|
||||
#define B460800 0x00001004
|
||||
#define B500000 0x00001005
|
||||
#define B576000 0x00001006
|
||||
#define B921600 0x00001007
|
||||
#define B1000000 0x00001008
|
||||
#define B1152000 0x00001009
|
||||
#define B1500000 0x0000100a
|
||||
#define B2000000 0x0000100b
|
||||
#define B2500000 0x0000100c
|
||||
#define B3000000 0x0000100d
|
||||
#define B3500000 0x0000100e
|
||||
#define B4000000 0x0000100f
|
||||
|
||||
/* ---- tcsetattr optional_actions ---- */
|
||||
|
||||
#define TCSANOW 0 /* change immediately */
|
||||
#define TCSADRAIN 1 /* change after output drains */
|
||||
#define TCSAFLUSH 2 /* drain output and discard input */
|
||||
|
||||
/* ---- tcflush queue_selector ---- */
|
||||
|
||||
#define TCIFLUSH 0 /* discard data received but not yet read */
|
||||
#define TCOFLUSH 1 /* discard data written but not yet transmitted */
|
||||
#define TCIOFLUSH 2 /* discard both */
|
||||
|
||||
/* ---- tcflow action ---- */
|
||||
|
||||
#define TCOOFF 0 /* suspend output */
|
||||
#define TCOON 1 /* restart suspended output */
|
||||
#define TCIOFF 2 /* transmit a STOP character */
|
||||
#define TCION 3 /* transmit a START character */
|
||||
|
||||
/* ---- Level 1 (POSIX base) ---- */
|
||||
|
||||
/*
|
||||
* Store the terminal attributes of the terminal open on fildes into
|
||||
* *termios_p. Return 0 on success, or -1 with errno set (ENOTTY when
|
||||
* fildes is not a terminal, EBADF on a bad descriptor).
|
||||
*/
|
||||
int
|
||||
tcgetattr(int fildes, struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Set the terminal attributes of the terminal open on fildes from
|
||||
* *termios_p, applying optional_actions (TCSANOW apply immediately,
|
||||
* TCSADRAIN wait for output to drain, TCSAFLUSH drain and then discard
|
||||
* unread input). Return 0 on success, or -1 with errno set (EINVAL for an
|
||||
* invalid optional_actions, ENOTTY/EBADF as for tcgetattr).
|
||||
*/
|
||||
int
|
||||
tcsetattr(int fildes, int optional_actions, const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Transmit a break condition on the terminal open on fildes. The duration
|
||||
* argument is ignored (POSIX leaves a nonzero duration
|
||||
* implementation-defined). Return 0 on success or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
tcsendbreak(int fildes, int duration);
|
||||
|
||||
/*
|
||||
* Wait until all output written to the terminal open on fildes has been
|
||||
* transmitted. Return 0 on success or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
tcdrain(int fildes);
|
||||
|
||||
/*
|
||||
* Discard data written but not yet transmitted (TCOFLUSH), data received
|
||||
* but not yet read (TCIFLUSH), or both (TCIOFLUSH) on the terminal open on
|
||||
* fildes. Return 0 on success or -1 with errno set (EINVAL for an invalid
|
||||
* queue_selector).
|
||||
*/
|
||||
int
|
||||
tcflush(int fildes, int queue_selector);
|
||||
|
||||
/*
|
||||
* Suspend or restart transmission (TCOOFF/TCOON) or transmit a STOP/START
|
||||
* character (TCIOFF/TCION) on the terminal open on fildes. Return 0 on
|
||||
* success or -1 with errno set (EINVAL for an invalid action).
|
||||
*/
|
||||
int
|
||||
tcflow(int fildes, int action);
|
||||
|
||||
/*
|
||||
* Return the input baud rate stored in *termios_p (a B* constant), as
|
||||
* encoded in the c_cflag CIBAUD field, or B0 when the input rate is not
|
||||
* separately stored (the kernel then treats it as equal to the output
|
||||
* rate, and cfsetispeed with B0 selects exactly that).
|
||||
*/
|
||||
speed_t
|
||||
cfgetispeed(const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Return the output baud rate stored in *termios_p (a B* constant), as
|
||||
* encoded in the c_cflag CBAUD field.
|
||||
*/
|
||||
speed_t
|
||||
cfgetospeed(const struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Store speed as the input baud rate of *termios_p. Accept any value that
|
||||
* fits the CBAUD encoding (B0 means "input rate follows the output rate");
|
||||
* an out-of-range speed yields -1 with errno EINVAL.
|
||||
*/
|
||||
int
|
||||
cfsetispeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
/*
|
||||
* Store speed as the output baud rate of *termios_p. Accept any value that
|
||||
* fits the CBAUD encoding (see cfsetispeed); an out-of-range speed yields
|
||||
* -1 with errno EINVAL.
|
||||
*/
|
||||
int
|
||||
cfsetospeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* Level 2 (muslmimic): BSD/glibc extensions and XSI [CX] additions. */
|
||||
|
||||
/*
|
||||
* Set *termios_p to the "raw" mode of the old Version-7 terminal driver:
|
||||
* input available character by character, echoing and all special
|
||||
* processing of input and output characters disabled (clear IGNBRK BRKINT
|
||||
* PARMRK ISTRIP INLCR IGNCR ICRNL IXON, clear OPOST, clear ECHO ECHONL
|
||||
* ICANON ISIG IEXTEN, select CS8 and no parity). Does not report errors.
|
||||
*/
|
||||
void
|
||||
cfmakeraw(struct termios *termios_p);
|
||||
|
||||
/*
|
||||
* Store speed as both the input and the output baud rate of *termios_p
|
||||
* (cfsetospeed then cfsetispeed with the same value). Return 0 on success
|
||||
* or -1 with errno EINVAL for an out-of-range speed.
|
||||
*/
|
||||
int
|
||||
cfsetspeed(struct termios *termios_p, speed_t speed);
|
||||
|
||||
/*
|
||||
* Return the session id of the session that has the terminal open on
|
||||
* fildes as its controlling terminal, or -1 with errno set when fildes is
|
||||
* not a controlling terminal (ENOTTY).
|
||||
*/
|
||||
pid_t
|
||||
tcgetsid(int fildes);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_TERMIOS_H */
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef VLIBC_TGMATH_H
|
||||
#define VLIBC_TGMATH_H
|
||||
|
||||
/*
|
||||
* vlibc — <tgmath.h>.
|
||||
*
|
||||
* Type-generic math (C23 7.25). Each macro inspects the type of its
|
||||
* controlling expression and dispatches to the matching precision of the
|
||||
* real <math.h> function: a float argument selects the `...f` variant, a
|
||||
* long double argument the `...l` variant, and everything else (double,
|
||||
* any integer type, and the _Complex types, whose imaginary part is
|
||||
* dropped by the usual argument conversion) the unsuffixed double
|
||||
* variant. The controlling expression is the first floating argument, so
|
||||
* the two-argument forms dispatch on x and require y to be convertible to
|
||||
* x's type.
|
||||
*
|
||||
* This header is ISO C core and is present in every profile. The
|
||||
* classification macros (fpclassify/isnan/isinf/isfinite/isnormal/signbit)
|
||||
* need no dispatch of their own — <math.h> already defines them over
|
||||
* GCC's type-generic builtins, so including <math.h> below makes them
|
||||
* work unchanged for every real and complex type.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* fabs */
|
||||
#define fabs(x) _Generic((x), float: fabsf, long double: fabsl, default: fabs)(x)
|
||||
|
||||
/* copysign */
|
||||
#define copysign(x, y) \
|
||||
_Generic((x), float: copysignf, long double: copysignl, default: copysign)(x, y)
|
||||
|
||||
/* rounding toward zero / ±Inf / current mode (floor, ceil, trunc, round,
|
||||
* rint, nearbyint) */
|
||||
#define floor(x) _Generic((x), float: floorf, long double: floorl, default: floor)(x)
|
||||
#define ceil(x) _Generic((x), float: ceilf, long double: ceill, default: ceil)(x)
|
||||
#define trunc(x) _Generic((x), float: truncf, long double: truncl, default: trunc)(x)
|
||||
#define round(x) _Generic((x), float: roundf, long double: roundl, default: round)(x)
|
||||
#define rint(x) _Generic((x), float: rintf, long double: rintl, default: rint)(x)
|
||||
#define nearbyint(x) \
|
||||
_Generic((x), float: nearbyintf, long double: nearbyintl, default: nearbyint)(x)
|
||||
|
||||
/* integer-returning rounding (lrint, llrint, lround, llround) */
|
||||
#define lrint(x) _Generic((x), float: lrintf, long double: lrintl, default: lrint)(x)
|
||||
#define llrint(x) _Generic((x), float: llrintf, long double: llrintl, default: llrint)(x)
|
||||
#define lround(x) _Generic((x), float: lroundf, long double: lroundl, default: lround)(x)
|
||||
#define llround(x) _Generic((x), float: llroundf, long double: llroundl, default: llround)(x)
|
||||
|
||||
/* fraction/exponent splitting (frexp, modf) and exponent scaling (ldexp,
|
||||
* scalbn, scalbln): the second argument is an int/long or a pointer to
|
||||
* the first argument's type and follows x's precision automatically */
|
||||
#define frexp(x, e) _Generic((x), float: frexpf, long double: frexpl, default: frexp)(x, e)
|
||||
#define modf(x, i) _Generic((x), float: modff, long double: modfl, default: modf)(x, i)
|
||||
#define ldexp(x, n) _Generic((x), float: ldexpf, long double: ldexpl, default: ldexp)(x, n)
|
||||
#define scalbn(x, n) _Generic((x), float: scalbnf, long double: scalbnl, default: scalbn)(x, n)
|
||||
#define scalbln(x, n) _Generic((x), float: scalblnf, long double: scalblnl, default: scalbln)(x, n)
|
||||
|
||||
/* minimum/maximum/difference */
|
||||
#define fmin(x, y) _Generic((x), float: fminf, long double: fminl, default: fmin)(x, y)
|
||||
#define fmax(x, y) _Generic((x), float: fmaxf, long double: fmaxl, default: fmax)(x, y)
|
||||
#define fdim(x, y) _Generic((x), float: fdimf, long double: fdiml, default: fdim)(x, y)
|
||||
|
||||
/* remainder (fmod, remainder, remquo: the trailing int* follows x's
|
||||
* precision) */
|
||||
#define fmod(x, y) _Generic((x), float: fmodf, long double: fmodl, default: fmod)(x, y)
|
||||
#define remainder(x, y) \
|
||||
_Generic((x), float: remainderf, long double: remainderl, default: remainder)(x, y)
|
||||
#define remquo(x, y, q) \
|
||||
_Generic((x), float: remquof, long double: remquol, default: remquo)(x, y, q)
|
||||
|
||||
/* exponent reading (ilogb returns int, logb returns x's precision) */
|
||||
#define ilogb(x) _Generic((x), float: ilogbf, long double: ilogbl, default: ilogb)(x)
|
||||
#define logb(x) _Generic((x), float: logbf, long double: logbl, default: logb)(x)
|
||||
|
||||
#endif /* VLIBC_TGMATH_H */
|
||||
@@ -0,0 +1,190 @@
|
||||
#ifndef VLIBC_THREADS_H
|
||||
#define VLIBC_THREADS_H
|
||||
|
||||
/*
|
||||
* vlibc — <threads.h>.
|
||||
*
|
||||
* C11 threads. STUB: the header lands here (level-1 gate) but every
|
||||
* function is implemented by the threads todo (#45), which wraps them as
|
||||
* thin aliases over the pthread layer. Until then, user code compiles
|
||||
* against these declarations and linking resolves once #45 lands.
|
||||
*
|
||||
* The types are placeholder shapes for now; #45 owns their final form (the
|
||||
* pthread-backed layout) and may refine them in place.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#if VLIBC_HAS_HEADER_THREADS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forward-declared: the real definition lives in <time.h> (later todo). */
|
||||
struct timespec;
|
||||
|
||||
/* Thread identifier (x86_64: matches pthread_t's unsigned long). */
|
||||
typedef unsigned long thrd_t;
|
||||
|
||||
/* Start routine of a thread. */
|
||||
typedef int (*thrd_start_t)(void *);
|
||||
|
||||
/* Destructor run when a thread exits. */
|
||||
typedef void (*tss_dtor_t)(void *);
|
||||
|
||||
/* Mutex. */
|
||||
typedef struct
|
||||
{
|
||||
unsigned long opaque;
|
||||
} mtx_t;
|
||||
|
||||
/* Condition variable. */
|
||||
typedef struct
|
||||
{
|
||||
unsigned long opaque;
|
||||
} cnd_t;
|
||||
|
||||
/* Thread-specific storage key. */
|
||||
typedef struct
|
||||
{
|
||||
unsigned long opaque;
|
||||
} tss_t;
|
||||
|
||||
/* call_once state. */
|
||||
typedef struct
|
||||
{
|
||||
unsigned long opaque;
|
||||
} once_flag;
|
||||
|
||||
/* Initializer for a once_flag object. */
|
||||
#define ONCE_FLAG_INIT {0}
|
||||
|
||||
/* Upper bound on tss_dtor_t invocations per key at thread exit. */
|
||||
#define TSS_DTOR_ITERATIONS 4
|
||||
|
||||
/* Mutex kinds for mtx_init. */
|
||||
enum
|
||||
{
|
||||
mtx_plain = 0,
|
||||
mtx_recursive = 1,
|
||||
mtx_timed = 2
|
||||
};
|
||||
|
||||
/* Result codes shared by the thrd_* and mtx_* functions. */
|
||||
enum
|
||||
{
|
||||
thrd_success = 0,
|
||||
thrd_busy = 1,
|
||||
thrd_error = 2,
|
||||
thrd_nomem = 3,
|
||||
thrd_timedout = 4
|
||||
};
|
||||
|
||||
/* Create a thread running func(arg); stores its id through thr. */
|
||||
int
|
||||
thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
|
||||
|
||||
/* Non-zero iff lhs and rhs name the same thread. */
|
||||
int
|
||||
thrd_equal(thrd_t lhs, thrd_t rhs);
|
||||
|
||||
/* Identifier of the calling thread. */
|
||||
thrd_t
|
||||
thrd_current(void);
|
||||
|
||||
/* Sleep for the interval in *duration; *remaining gets the unslept part. */
|
||||
int
|
||||
thrd_sleep(const struct timespec *duration, struct timespec *remaining);
|
||||
|
||||
/* Yield the processor to other runnable threads. */
|
||||
void
|
||||
thrd_yield(void);
|
||||
|
||||
/* Terminate the calling thread, reporting res. */
|
||||
_Noreturn void
|
||||
thrd_exit(int res);
|
||||
|
||||
/* Detach thr so its resources are released on exit. */
|
||||
int
|
||||
thrd_detach(thrd_t thr);
|
||||
|
||||
/* Wait for thr to exit; stores its result through *res when non-NULL. */
|
||||
int
|
||||
thrd_join(thrd_t thr, int *res);
|
||||
|
||||
/* Initialize *mtx with the given kind. */
|
||||
int
|
||||
mtx_init(mtx_t *mtx, int type);
|
||||
|
||||
/* Lock *mtx, blocking if needed. */
|
||||
int
|
||||
mtx_lock(mtx_t *mtx);
|
||||
|
||||
/* Lock *mtx, blocking at most until *ts. */
|
||||
int
|
||||
mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts);
|
||||
|
||||
/* Lock *mtx unless another thread holds it (thrd_busy). */
|
||||
int
|
||||
mtx_trylock(mtx_t *mtx);
|
||||
|
||||
/* Unlock *mtx. */
|
||||
int
|
||||
mtx_unlock(mtx_t *mtx);
|
||||
|
||||
/* Release *mtx's resources. */
|
||||
void
|
||||
mtx_destroy(mtx_t *mtx);
|
||||
|
||||
/* Run func exactly once per once_flag object. */
|
||||
void
|
||||
call_once(once_flag *flag, void (*func)(void));
|
||||
|
||||
/* Initialize *cnd. */
|
||||
int
|
||||
cnd_init(cnd_t *cnd);
|
||||
|
||||
/* Wake one thread waiting on *cnd. */
|
||||
int
|
||||
cnd_signal(cnd_t *cnd);
|
||||
|
||||
/* Wake all threads waiting on *cnd. */
|
||||
int
|
||||
cnd_broadcast(cnd_t *cnd);
|
||||
|
||||
/* Wait on *cnd; *mtx must be locked by the caller. */
|
||||
int
|
||||
cnd_wait(cnd_t *cnd, mtx_t *mtx);
|
||||
|
||||
/* Wait on *cnd at most until *ts. */
|
||||
int
|
||||
cnd_timedwait(cnd_t *restrict cnd, mtx_t *restrict mtx, const struct timespec *restrict ts);
|
||||
|
||||
/* Release *cnd's resources. */
|
||||
void
|
||||
cnd_destroy(cnd_t *cnd);
|
||||
|
||||
/* Create a thread-specific key *key; dtor may be NULL. */
|
||||
int
|
||||
tss_create(tss_t *key, tss_dtor_t dtor);
|
||||
|
||||
/* Value stored for *key in the calling thread (NULL when unset). */
|
||||
void *
|
||||
tss_get(tss_t key);
|
||||
|
||||
/* Store val under *key for the calling thread. */
|
||||
int
|
||||
tss_set(tss_t key, void *val);
|
||||
|
||||
/* Release *key; the key may not be used afterwards. */
|
||||
void
|
||||
tss_delete(tss_t key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_HAS_HEADER_THREADS_H */
|
||||
|
||||
#endif /* VLIBC_THREADS_H */
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
#ifndef VLIBC_TIME_H
|
||||
#define VLIBC_TIME_H
|
||||
|
||||
/*
|
||||
* vlibc — <time.h>.
|
||||
*
|
||||
* Calendar time, clock ids, and interval timers. The ISO C functions
|
||||
* (time, clock, timespec_get, difftime, asctime/ctime/mktime/gmtime/
|
||||
* localtime, strftime) and the POSIX clock_* / timer_* / sleep family are
|
||||
* thin, level-gated wrappers over the Linux time syscalls.
|
||||
*
|
||||
* Level 1 (onlyposix): time, clock, timespec_get, difftime,
|
||||
* clock_gettime/settime/getres/getcpuclockid,
|
||||
* clock_nanosleep, nanosleep, sleep, and the
|
||||
* broken-down-time/formatting declarations
|
||||
* (mktime, gmtime/localtime and _r forms, asctime,
|
||||
* ctime, strftime, tzset).
|
||||
* Level 2 (muslmimic): POSIX interval timers (timer_create/delete/
|
||||
* settime/gettime/getoverrun) and the XSI
|
||||
* conveniences (timegm, strftime_l, usleep,
|
||||
* asctime_r, ctime_r, strptime, tzname/daylight/
|
||||
* timezone).
|
||||
*
|
||||
* The scalar time types (time_t, clock_t, clockid_t, pid_t, timer_t and
|
||||
* useconds_t) come from <sys/types.h>, which this header includes; struct
|
||||
* timespec and struct tm are defined here. timer_t is the <sys/types.h>
|
||||
* opaque pointer type (kernel timer ids are returned through it); struct
|
||||
* sigevent and struct itimerspec stay forward-declared here — their kernel
|
||||
* ABI mirrors are private to the timer implementation (see src/time/
|
||||
* timer.c) and to consumers that define matching structs.
|
||||
*
|
||||
* The clock ids and TIMER_ABSTIME mirror the kernel ABI (x86_64): the
|
||||
* CLOCK_* values are passed to SYS_clock_* and SYS_timer_* unchanged, and
|
||||
* TIMER_ABSTIME is the timer_settime/clock_nanosleep flag bit.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Base argument of timespec_get(); the only base defined by ISO C. */
|
||||
#define TIME_UTC 1
|
||||
|
||||
/* Ticks per second reported by clock() (vlibc always reports 1 MHz). */
|
||||
#define CLOCKS_PER_SEC 1000000
|
||||
|
||||
/* Clock ids (kernel ABI; SYS_clock_* argument). */
|
||||
#define CLOCK_REALTIME 0 /* wall clock since the Epoch */
|
||||
#define CLOCK_MONOTONIC 1 /* monotonic since boot */
|
||||
#define CLOCK_PROCESS_CPUTIME_ID 2 /* CPU time of this process */
|
||||
#define CLOCK_THREAD_CPUTIME_ID 3 /* CPU time of this thread */
|
||||
|
||||
/* Flag for absolute (vs relative) timer expiry. */
|
||||
#define TIMER_ABSTIME 1
|
||||
|
||||
/*
|
||||
* Elapsed time, seconds + nanoseconds. Kernel ABI layout (x86_64): the
|
||||
* wrappers pass this straight to the clock/nanosleep/timer syscalls.
|
||||
*/
|
||||
struct timespec
|
||||
{
|
||||
time_t tv_sec; /* seconds */
|
||||
long tv_nsec; /* nanoseconds (0..999999999) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Broken-down civil time. tm_gmtoff and tm_zone are the XSI extensions
|
||||
* (seconds east of UTC / timezone name) carried for glibc compatibility.
|
||||
*/
|
||||
struct tm
|
||||
{
|
||||
int tm_sec; /* seconds after the minute (0..60) */
|
||||
int tm_min; /* minutes after the hour (0..59) */
|
||||
int tm_hour; /* hours since midnight (0..23) */
|
||||
int tm_mday; /* day of the month (1..31) */
|
||||
int tm_mon; /* months since January (0..11) */
|
||||
int tm_year; /* years since 1900 */
|
||||
int tm_wday; /* days since Sunday (0..6) */
|
||||
int tm_yday; /* days since January 1 (0..365) */
|
||||
int tm_isdst; /* daylight-saving flag */
|
||||
long tm_gmtoff; /* seconds east of UTC */
|
||||
const char *tm_zone; /* timezone abbreviation */
|
||||
};
|
||||
|
||||
/* Return the current wall-clock time in seconds; if t is not NULL store it
|
||||
* there too, and return (time_t)-1 with errno set on failure. */
|
||||
time_t
|
||||
time(time_t *t);
|
||||
|
||||
/* Approximate processor time in CLOCKS_PER_SEC units since an arbitrary
|
||||
* epoch, or (clock_t)-1 with errno set on failure. */
|
||||
clock_t
|
||||
clock(void);
|
||||
|
||||
/* Store the current time for base into ts and return base; return 0 if
|
||||
* base is not TIME_UTC or the clock cannot be read. */
|
||||
int
|
||||
timespec_get(struct timespec *ts, int base);
|
||||
|
||||
/* Read the given clock into tp. Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_gettime(clockid_t clockid, struct timespec *tp);
|
||||
|
||||
/* Set the given clock from tp. Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_settime(clockid_t clockid, const struct timespec *tp);
|
||||
|
||||
/* Resolution of the given clock; store it in res when res is not NULL.
|
||||
* Return 0 or -1 with errno set. */
|
||||
int
|
||||
clock_getres(clockid_t clockid, struct timespec *res);
|
||||
|
||||
/* Clock id measuring the CPU time of the given process (0 = calling
|
||||
* process). Return 0, or -1 with errno set when the process is unknown. */
|
||||
int
|
||||
clock_getcpuclockid(pid_t pid, clockid_t *clock_id);
|
||||
|
||||
/* Sleep on the given clock. Without TIMER_ABSTIME request is relative and
|
||||
* the sleep restarts from remain on signal interruption. */
|
||||
int
|
||||
clock_nanosleep(clockid_t clockid, int flags, const struct timespec *request,
|
||||
struct timespec *remain);
|
||||
|
||||
/* Sleep request seconds; when interrupted and remain is not NULL the sleep
|
||||
* restarts from the remaining time. Return 0 or -1 with errno set. */
|
||||
int
|
||||
nanosleep(const struct timespec *request, struct timespec *remain);
|
||||
|
||||
/* Sleep seconds (whole seconds; signals can shorten the sleep, which is
|
||||
* then resumed). Return the unslept seconds, normally 0. */
|
||||
unsigned int
|
||||
sleep(unsigned int seconds);
|
||||
|
||||
/* Convert broken-down civil time back to calendar seconds. */
|
||||
time_t
|
||||
mktime(struct tm *tm);
|
||||
|
||||
/* Broken-down local time; localtime uses an internal static buffer.
|
||||
* Return NULL on error. */
|
||||
struct tm *
|
||||
localtime(const time_t *timer);
|
||||
|
||||
/* Broken-down local time into buf. Return buf or NULL on error. */
|
||||
struct tm *
|
||||
localtime_r(const time_t *timer, struct tm *buf);
|
||||
|
||||
/* Broken-down UTC; gmtime uses an internal static buffer. */
|
||||
struct tm *
|
||||
gmtime(const time_t *timer);
|
||||
|
||||
/* Broken-down UTC into buf. Return buf or NULL on error. */
|
||||
struct tm *
|
||||
gmtime_r(const time_t *timer, struct tm *buf);
|
||||
|
||||
/* Fixed-format rendering of broken-down time ("Sun Sep 16 01:03:52 1973"). */
|
||||
char *
|
||||
asctime(const struct tm *tm);
|
||||
|
||||
/* asctime(localtime(timer)); fixed-format wall-clock rendering. */
|
||||
char *
|
||||
ctime(const time_t *timer);
|
||||
|
||||
/* Format broken-down time per format into s (at most maxsize bytes);
|
||||
* return the bytes written (0 when the buffer was too small). */
|
||||
size_t
|
||||
strftime(char *restrict s, size_t maxsize, const char *restrict format,
|
||||
const struct tm *restrict timeptr);
|
||||
|
||||
/* Seconds between two calendar times (b subtracted from a). */
|
||||
double
|
||||
difftime(time_t a, time_t b);
|
||||
|
||||
/* Establish the local timezone from TZ / the system default. */
|
||||
void
|
||||
tzset(void);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): POSIX interval timers and XSI conveniences. */
|
||||
|
||||
/* Opaque kernel-ABI structures; consumers define layout-compatible structs
|
||||
* (see src/time/timer.c). */
|
||||
struct sigevent;
|
||||
struct itimerspec;
|
||||
|
||||
/* Create a per-process interval timer on clockid; the timer id is stored
|
||||
* through timerid. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_create(clockid_t clockid, const struct sigevent *restrict evp, timer_t *restrict timerid);
|
||||
|
||||
/* Destroy the given timer. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_delete(timer_t timerid);
|
||||
|
||||
/* Arm/disarm the timer; when old_value is not NULL the previous setting is
|
||||
* stored there. Return 0 or -1 with errno set. */
|
||||
int
|
||||
timer_settime(timer_t timerid, int flags, const struct itimerspec *restrict new_value,
|
||||
struct itimerspec *restrict old_value);
|
||||
|
||||
/* Remaining time until the timer expires (and its interval). Return 0 or
|
||||
* -1 with errno set. */
|
||||
int
|
||||
timer_gettime(timer_t timerid, struct itimerspec *curr_value);
|
||||
|
||||
/* Number of timer expirations between the most recent signal delivery and
|
||||
* the one before it; -1 with errno set when there was no pending delivery. */
|
||||
int
|
||||
timer_getoverrun(timer_t timerid);
|
||||
|
||||
/* mktime in UTC rather than local time. */
|
||||
time_t
|
||||
timegm(struct tm *tm);
|
||||
|
||||
/* strftime with an explicit locale object (locale_t is not yet defined;
|
||||
* the locale argument is an opaque pointer). */
|
||||
size_t
|
||||
strftime_l(char *restrict s, size_t maxsize, const char *restrict format,
|
||||
const struct tm *restrict timeptr, void *loc);
|
||||
|
||||
/* Microsecond sleep (whole microseconds; signals resume the sleep).
|
||||
* Return 0 or the unslept microseconds with errno set. */
|
||||
useconds_t
|
||||
usleep(useconds_t usec);
|
||||
|
||||
/* asctime into a caller-provided buffer of at least 26 bytes. */
|
||||
char *
|
||||
asctime_r(const struct tm *tm, char *buf);
|
||||
|
||||
/* ctime into a caller-provided buffer of at least 26 bytes. */
|
||||
char *
|
||||
ctime_r(const time_t *timer, char *buf);
|
||||
|
||||
/* Parse format from s into broken-down time; return the first character
|
||||
* not consumed, or NULL when the format did not match. */
|
||||
char *
|
||||
strptime(const char *restrict s, const char *restrict format, struct tm *restrict tm);
|
||||
|
||||
/* Timezone state maintained by tzset(). */
|
||||
extern char *tzname[2];
|
||||
extern int daylight;
|
||||
extern long timezone;
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_TIME_H */
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef VLIBC_UCHAR_H
|
||||
#define VLIBC_UCHAR_H
|
||||
|
||||
/*
|
||||
* vlibc — <uchar.h>.
|
||||
*
|
||||
* Unicode character types and the multibyte conversion declarations (C11,
|
||||
* gated at level 1 per the plan's header inventory). In C23 char16_t and
|
||||
* char32_t are keywords, so the typedefs below only fire in C17 (and
|
||||
* older); the C17 types are the compiler's __CHAR16_TYPE__/__CHAR32_TYPE__,
|
||||
* which is what the C23 keywords name as well.
|
||||
*
|
||||
* The mbrtoc16/mbrtoc32/c16rtomb/c32rtomb functions are DECLARED here but
|
||||
* implemented by the multibyte todo (#50): user code compiles against this
|
||||
* header today and linking resolves once #50 lands.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if VLIBC_HAS_HEADER_UCHAR_H
|
||||
|
||||
/*
|
||||
* char16_t/char32_t remain typedefs in C23 (unlike bool/alignas/... they were
|
||||
* NOT promoted to keywords), so they are defined in every C mode from the
|
||||
* compiler's __CHAR16_TYPE__/__CHAR32_TYPE__; in C++ they are keywords and
|
||||
* no typedef fires.
|
||||
*/
|
||||
#ifndef __cplusplus
|
||||
typedef __CHAR16_TYPE__ char16_t;
|
||||
typedef __CHAR32_TYPE__ char32_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Conversion state for multibyte conversions. Two opaque words; the real
|
||||
* layout is owned by the multibyte todo (#50), which may extend this
|
||||
* definition when it lands (see the learnings note).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int state[2];
|
||||
} mbstate_t;
|
||||
|
||||
/*
|
||||
* Multibyte <-> UTF-16/UTF-32 conversions (implemented in #50).
|
||||
*
|
||||
* mbrtoc16: convert at most n bytes of the multibyte string s to a char16_t
|
||||
* stored through pc16, consuming the corresponding number of bytes.
|
||||
* c16rtomb: convert char16_t c16 to at most MB_CUR_MAX bytes at s.
|
||||
* The char32_t forms are the same for UTF-32.
|
||||
*/
|
||||
size_t
|
||||
mbrtoc16(char16_t *restrict pc16, const char *restrict s, size_t n, mbstate_t *restrict ps);
|
||||
size_t
|
||||
c16rtomb(char *restrict s, char16_t c16, mbstate_t *restrict ps);
|
||||
size_t
|
||||
mbrtoc32(char32_t *restrict pc32, const char *restrict s, size_t n, mbstate_t *restrict ps);
|
||||
size_t
|
||||
c32rtomb(char *restrict s, char32_t c32, mbstate_t *restrict ps);
|
||||
|
||||
#endif /* VLIBC_HAS_HEADER_UCHAR_H */
|
||||
|
||||
#endif /* VLIBC_UCHAR_H */
|
||||
@@ -0,0 +1,816 @@
|
||||
#ifndef VLIBC_UNISTD_H
|
||||
#define VLIBC_UNISTD_H
|
||||
|
||||
/*
|
||||
* vlibc — <unistd.h>.
|
||||
*
|
||||
* File descriptors, file I/O, and the access/whence symbolic constants
|
||||
* (POSIX.1-2008). Every function here is an unbuffered pass-through to the
|
||||
* kernel: failures are reported as -1 (or the fd/offset on success) with
|
||||
* errno set by the syscall layer.
|
||||
*
|
||||
* Level 1 (onlyposix): read, write, pread, pwrite, open, openat, close,
|
||||
* lseek, dup, dup2, pipe, fsync, fdatasync, ftruncate,
|
||||
* sync, access, faccessat.
|
||||
* Level 2 (muslmimic): dup3, pipe2 (Linux extensions), truncate (XSI),
|
||||
* lseek64 (glibc LFS alias of lseek on x86_64).
|
||||
*
|
||||
* Process control (todo 20) is declared further down:
|
||||
*
|
||||
* Level 1 (onlyposix): fork, exec family (execl/execlp/execle/execv/
|
||||
* execvp/execve/fexecve), getpid, getppid, getuid,
|
||||
* geteuid, getgid, getegid, setuid, seteuid, setgid,
|
||||
* setegid, getgroups, setpgid, getpgrp, setsid.
|
||||
* Level 2 (muslmimic): vfork, setpgrp (obsolescent), setgroups, getpgid,
|
||||
* getsid, tcgetpgrp, tcsetpgrp (XSI).
|
||||
*
|
||||
* System limits and options (todo 32) are declared after the process
|
||||
* block:
|
||||
*
|
||||
* Level 1 (onlyposix): sysconf, pathconf, fpathconf, the _SC_* and _PC_*
|
||||
* configuration keys.
|
||||
* Level 2 (muslmimic): gethostname, sethostname, gethostid (XSI/BSD
|
||||
* system identification — NOT POSIX.1-2008 base),
|
||||
* and the _CS_* keys for confstr().
|
||||
*
|
||||
* The current-working-directory, link/symlink/readlink, path-removal and
|
||||
* *at operations, the terminal-identity and login functions, getopt and
|
||||
* confstr (todo 38) come after the system-limits block:
|
||||
*
|
||||
* Level 1 (onlyposix): getcwd, chdir, fchdir, readlink, symlink, link,
|
||||
* unlink, rmdir, linkat, readlinkat, renameat,
|
||||
* symlinkat, unlinkat, isatty, ttyname, ttyname_r,
|
||||
* getlogin, getlogin_r, getopt plus the optarg/
|
||||
* optind/opterr/optopt globals, and confstr (its
|
||||
* _CS_* key constants are level 2, above).
|
||||
* Level 2 (muslmimic): getwd (obsolescent), chroot, swab (XSI).
|
||||
* basename/dirname live in <libgen.h> and realpath
|
||||
* in <stdlib.h> (their POSIX homes), also XSI.
|
||||
*
|
||||
* uname() and struct utsname live in <sys/utsname.h> (todo 32).
|
||||
*
|
||||
* The open-flag constants (O_RDONLY, O_CREAT, O_CLOEXEC, ...) belong to
|
||||
* <fcntl.h> and are deliberately not defined here; the oflag arguments below
|
||||
* are plain int and take their values from that header. The optional mode
|
||||
* argument of open/openat is a mode_t supplied only when oflag contains
|
||||
* O_CREAT or O_TMPFILE.
|
||||
*
|
||||
* None of these declarations carry an intent attribute: every function
|
||||
* performs I/O with side effects and reports failures through errno, so
|
||||
* const/pure would be unsound. The process functions additionally must not
|
||||
* be const/pure because their results are process state — marking getpid
|
||||
* const, for example, would let the compiler hoist it across fork() and
|
||||
* observe the parent's pid inside the child.
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Level 1 (POSIX base). */
|
||||
|
||||
/* Access-check modes for access() and faccessat(). */
|
||||
#define F_OK 0 /* existence only */
|
||||
#define X_OK 1 /* execute (search for a directory) */
|
||||
#define W_OK 2 /* write */
|
||||
#define R_OK 4 /* read */
|
||||
|
||||
/* whence values for lseek() and lseek64() (also defined by <stdio.h>). */
|
||||
#define SEEK_SET 0 /* from the beginning of the file */
|
||||
#define SEEK_CUR 1 /* from the current position */
|
||||
#define SEEK_END 2 /* from the end of the file */
|
||||
|
||||
/*
|
||||
* Read up to nbyte bytes from fildes into buf and return the number of
|
||||
* bytes read, 0 at end of file, or -1 with errno set on error. Unbuffered.
|
||||
*/
|
||||
ssize_t
|
||||
read(int fildes, void *buf, size_t nbyte);
|
||||
|
||||
/*
|
||||
* Write up to nbyte bytes from buf to fildes and return the number of
|
||||
* bytes written, or -1 with errno set on error. Unbuffered.
|
||||
*/
|
||||
ssize_t
|
||||
write(int fildes, const void *buf, size_t nbyte);
|
||||
|
||||
/*
|
||||
* Read nbyte bytes from fildes starting at offset, without changing the
|
||||
* file position; return the number of bytes read, or -1 with errno set.
|
||||
*/
|
||||
ssize_t
|
||||
pread(int fildes, void *buf, size_t nbyte, off_t offset);
|
||||
|
||||
/*
|
||||
* Write nbyte bytes from buf to fildes starting at offset, without
|
||||
* changing the file position; return the number of bytes written, or -1
|
||||
* with errno set.
|
||||
*/
|
||||
ssize_t
|
||||
pwrite(int fildes, const void *buf, size_t nbyte, off_t offset);
|
||||
|
||||
/*
|
||||
* Open path with the oflag access mode (from <fcntl.h>) and return a file
|
||||
* descriptor, or -1 with errno set. A mode argument is required — and read
|
||||
* from the varargs — only when oflag contains O_CREAT or O_TMPFILE.
|
||||
*/
|
||||
int
|
||||
open(const char *path, int oflag, ...);
|
||||
|
||||
/*
|
||||
* Like open(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD from <fcntl.h> for the current working directory). The mode
|
||||
* varargs rule is the same as open().
|
||||
*/
|
||||
int
|
||||
openat(int fd, const char *path, int oflag, ...);
|
||||
|
||||
/*
|
||||
* Close the file descriptor fildes; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
close(int fildes);
|
||||
|
||||
/*
|
||||
* Reposition the file offset of fildes to offset per whence (SEEK_SET,
|
||||
* SEEK_CUR, SEEK_END) and return the resulting offset, or (off_t)-1 with
|
||||
* errno set. The full 64-bit offset is returned; errno is untouched on
|
||||
* success.
|
||||
*/
|
||||
off_t
|
||||
lseek(int fildes, off_t offset, int whence);
|
||||
|
||||
/*
|
||||
* Duplicate fildes to the lowest-numbered free descriptor; return it, or
|
||||
* -1 with errno set. The copy shares the file description (position,
|
||||
* flags, locks) with the original.
|
||||
*/
|
||||
int
|
||||
dup(int fildes);
|
||||
|
||||
/*
|
||||
* Duplicate fildes onto fildes2, closing fildes2 first if it was open;
|
||||
* return fildes2, or -1 with errno set. dup2(f, f) returns f without
|
||||
* doing anything (POSIX).
|
||||
*/
|
||||
int
|
||||
dup2(int fildes, int fildes2);
|
||||
|
||||
/*
|
||||
* Create a pipe: fildes[0] becomes the read end, fildes[1] the write end.
|
||||
* Return 0, or -1 with errno set. No descriptor flags are set (unlike
|
||||
* pipe2, this is plain POSIX).
|
||||
*/
|
||||
int
|
||||
pipe(int fildes[2]);
|
||||
|
||||
/*
|
||||
* Flush all buffered modifications of fildes and its metadata to stable
|
||||
* storage; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fsync(int fildes);
|
||||
|
||||
/*
|
||||
* Like fsync(), but may skip the metadata work needed only to preserve
|
||||
* file contents; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
fdatasync(int fildes);
|
||||
|
||||
/*
|
||||
* Truncate fildes to length bytes; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
ftruncate(int fildes, off_t length);
|
||||
|
||||
/*
|
||||
* Flush all filesystem caches to stable storage. Returns nothing.
|
||||
*/
|
||||
void
|
||||
sync(void);
|
||||
|
||||
/*
|
||||
* Check accessibility of path under amode (R_OK, W_OK, X_OK, F_OK); return
|
||||
* 0, or -1 with errno set. Uses the real IDs of the calling process.
|
||||
*/
|
||||
int
|
||||
access(const char *path, int amode);
|
||||
|
||||
/*
|
||||
* Like access(), but path is relative to the directory named by fd (use
|
||||
* AT_FDCWD for the current working directory) and flag may hold
|
||||
* AT_EACCESS; return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
faccessat(int fd, const char *path, int amode, int flag);
|
||||
|
||||
/* Level 1 (POSIX base): process control. */
|
||||
|
||||
/*
|
||||
* Create a child process that is a copy of the caller: fork() returns 0
|
||||
* in the child, the child's pid in the parent, and -1 with errno set on
|
||||
* failure.
|
||||
*
|
||||
* Around the fork, the atfork hook table runs: the prepare handlers in
|
||||
* reverse registration order before the fork, then the child handlers in
|
||||
* the child and the parent handlers in the parent, both in registration
|
||||
* order (POSIX pthread_atfork protocol). The table is registered by
|
||||
* pthread_atfork() (todo 45) and is empty — a no-op — in a process
|
||||
* without threads.
|
||||
*/
|
||||
pid_t
|
||||
fork(void);
|
||||
|
||||
/*
|
||||
* Replace the calling process image. Success never returns; -1 with
|
||||
* errno set otherwise. execl/execle/execlp take the arguments as a
|
||||
* varargs list terminated by (char *)NULL (execle is followed by one
|
||||
* final char *const envp[] argument); execv/execvp take an argv array.
|
||||
* execvp and execlp search the PATH environment variable (default
|
||||
* "/bin:/usr/bin") when file contains no '/'. execv/execvp/execl/execlp
|
||||
* use the caller's environment; execve/execle/fexecve take envp.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
int
|
||||
execl(const char *path, const char *arg0, ...);
|
||||
|
||||
int
|
||||
execle(const char *path, const char *arg0, ...);
|
||||
|
||||
int
|
||||
execlp(const char *file, const char *arg0, ...);
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
int
|
||||
execv(const char *path, char *const argv[]);
|
||||
|
||||
int
|
||||
execvp(const char *file, char *const argv[]);
|
||||
|
||||
int
|
||||
execve(const char *path, char *const argv[], char *const envp[]);
|
||||
|
||||
/*
|
||||
* Like execve, but the image is the open descriptor fd.
|
||||
*/
|
||||
int
|
||||
fexecve(int fd, char *const argv[], char *const envp[]);
|
||||
|
||||
/*
|
||||
* Process ids. getpid/getppid/getuid/geteuid/getgid/getegid read the
|
||||
* kernel's per-process ids; setuid/seteuid/setgid/setegid change them.
|
||||
* seteuid/setegid change only the effective id (setresuid/setresgid
|
||||
* underneath: Linux has no seteuid/setegid syscall).
|
||||
*/
|
||||
pid_t
|
||||
getpid(void);
|
||||
|
||||
pid_t
|
||||
getppid(void);
|
||||
|
||||
uid_t
|
||||
getuid(void);
|
||||
|
||||
uid_t
|
||||
geteuid(void);
|
||||
|
||||
gid_t
|
||||
getgid(void);
|
||||
|
||||
gid_t
|
||||
getegid(void);
|
||||
|
||||
int
|
||||
setuid(uid_t uid);
|
||||
|
||||
int
|
||||
seteuid(uid_t euid);
|
||||
|
||||
int
|
||||
setgid(gid_t gid);
|
||||
|
||||
int
|
||||
setegid(gid_t egid);
|
||||
|
||||
/*
|
||||
* Supplementary groups: getgroups fills grouplist with up to gidsetsize
|
||||
* group ids and returns the total count (0 can be passed to count only);
|
||||
* setgroups (level 2, XSI) installs the list.
|
||||
*/
|
||||
int
|
||||
getgroups(int gidsetsize, gid_t grouplist[]);
|
||||
|
||||
/*
|
||||
* Session control: setpgid moves pid into process group pgid (0 means
|
||||
* the caller / the caller's pid); getpgrp returns the caller's process
|
||||
* group; setsid creates a new session with the caller as leader and
|
||||
* returns the new session id.
|
||||
*/
|
||||
int
|
||||
setpgid(pid_t pid, pid_t pgid);
|
||||
|
||||
pid_t
|
||||
getpgrp(void);
|
||||
|
||||
pid_t
|
||||
setsid(void);
|
||||
|
||||
/* Level 1 (POSIX base): system limits and options. */
|
||||
|
||||
/*
|
||||
* The configuration keys for sysconf(), pathconf() and fpathconf(), and
|
||||
* (level 2) the _CS_* keys for confstr() (todo 38).
|
||||
*
|
||||
* POSIX defines the _SC_* and _PC_* names and the SEMANTICS of the values
|
||||
* the configuration functions return for them, but NOT their numeric
|
||||
* values: those are private to each C library (glibc numbers them
|
||||
* arbitrarily in <bits/confname.h>, musl in a private table). The values
|
||||
* below are vlibc's OWN stable ABI, assigned in declaration order and never
|
||||
* reused — program source must pass the symbolic constants, never literal
|
||||
* integers. They are deliberately NOT glibc's numbers. Any value outside
|
||||
* these enums is unknown to the configuration functions and yields -1
|
||||
* without errno (the unsupported-key convention, see below).
|
||||
*/
|
||||
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _SC_/_PC_ names */
|
||||
enum
|
||||
{
|
||||
/* Process and file limits (sysconf). */
|
||||
_SC_ARG_MAX = 0, /* max bytes of arguments + environment for exec */
|
||||
_SC_CHILD_MAX, /* max simultaneous processes per real user id */
|
||||
_SC_CLK_TCK, /* clock ticks per second, the times() unit (100) */
|
||||
_SC_NGROUPS_MAX, /* max supplementary group ids */
|
||||
_SC_OPEN_MAX, /* max open file descriptors per process */
|
||||
_SC_PAGESIZE, /* memory page size in bytes (from AT_PAGESZ) */
|
||||
_SC_PAGE_SIZE, /* POSIX synonym of _SC_PAGESIZE */
|
||||
_SC_NPROCESSORS_CONF, /* number of processors configured */
|
||||
_SC_NPROCESSORS_ONLN, /* number of processors online */
|
||||
_SC_PHYS_PAGES, /* number of physical memory pages */
|
||||
_SC_AVPHYS_PAGES, /* number of currently available memory pages */
|
||||
_SC_SYMLOOP_MAX, /* max symlink traversals while resolving a path */
|
||||
_SC_STREAM_MAX, /* max open standard I/O streams (FOPEN_MAX) */
|
||||
_SC_TZNAME_MAX, /* max bytes in a timezone name */
|
||||
_SC_VERSION, /* POSIX.1 version, 200809L */
|
||||
_SC_2_VERSION, /* POSIX.2 version, 200809L */
|
||||
_SC_JOB_CONTROL, /* 1 if job control is supported */
|
||||
_SC_SAVED_IDS, /* 1 if saved set-user/group-id is supported */
|
||||
_SC_HOST_NAME_MAX, /* max bytes in a host name (excluding NUL) */
|
||||
_SC_LOGIN_NAME_MAX, /* max bytes in a login name */
|
||||
_SC_GETPW_R_SIZE_MAX, /* recommended buffer size for getpw*_r */
|
||||
_SC_IOV_MAX, /* max iovec entries in readv/writev */
|
||||
_SC_LINE_MAX, /* max bytes in an input line (POSIX.2) */
|
||||
_SC_RE_DUP_MAX, /* max duplicate regexp interval counts */
|
||||
_SC_COLL_WEIGHTS_MAX, /* max weights for collating elements */
|
||||
_SC_EXPR_NEST_MAX, /* max expression nesting in expr(1) */
|
||||
|
||||
/* POSIX option indicators (sysconf). Values follow the _POSIX_*
|
||||
* convention: a version value means supported, -1 unsupported. */
|
||||
_SC_THREADS, /* _POSIX_THREADS */
|
||||
_SC_SEMAPHORES, /* _POSIX_SEMAPHORES */
|
||||
_SC_SHARED_MEMORY_OBJECTS, /* _POSIX_SHARED_MEMORY_OBJECTS */
|
||||
_SC_MESSAGE_PASSING, /* _POSIX_MESSAGE_PASSING */
|
||||
_SC_MONOTONIC_CLOCK, /* _POSIX_MONOTONIC_CLOCK */
|
||||
_SC_TIMERS, /* _POSIX_TIMERS */
|
||||
_SC_SYNCHRONIZED_IO, /* _POSIX_SYNCHRONIZED_IO */
|
||||
_SC_THREAD_SAFE_FUNCTIONS, /* _POSIX_THREAD_SAFE_FUNCTIONS */
|
||||
_SC_THREAD_ATTR_STACKADDR, /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
_SC_THREAD_ATTR_STACKSIZE, /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
_SC_THREAD_PRIORITY_SCHEDULING, /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
_SC_THREAD_PRIO_INHERIT, /* _POSIX_THREAD_PRIO_INHERIT */
|
||||
_SC_THREAD_PRIO_PROTECT, /* _POSIX_THREAD_PRIO_PROTECT */
|
||||
_SC_THREAD_PROCESS_SHARED, /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
_SC_REALTIME_SIGNALS, /* _POSIX_REALTIME_SIGNALS */
|
||||
|
||||
/* POSIX.1-2008 option indicators that use plain 1 for supported. */
|
||||
_SC_REGEXP, /* 1 if the RE functions are supported */
|
||||
_SC_SHELL, /* 1 if the shell is supported */
|
||||
_SC_XOPEN_VERSION, /* XSI version, 700 */
|
||||
_SC_XOPEN_UNIX, /* 1 if XSI interfaces are supported */
|
||||
_SC_XOPEN_CRYPT, /* 1 if the crypt function is available */
|
||||
_SC_XOPEN_ENH_I18N, /* 1 if enhanced internationalization is present */
|
||||
_SC_XOPEN_SHM, /* 1 if XSI shared memory is present */
|
||||
_SC_2_C_BIND, /* POSIX.2 C-language binding */
|
||||
_SC_2_C_DEV, /* POSIX.2 C-language development utilities */
|
||||
_SC_2_FORT_DEV, /* POSIX.2 FORTRAN development utilities */
|
||||
_SC_2_FORT_RUN, /* POSIX.2 FORTRAN runtime utilities */
|
||||
_SC_2_LOCALEDEF, /* POSIX.2 locale creation utilities */
|
||||
_SC_2_SW_DEV, /* POSIX.2 software development utilities */
|
||||
_SC_2_UPE /* POSIX.2 user-portability utilities */
|
||||
};
|
||||
|
||||
/*
|
||||
* The _PC_* keys for pathconf() and fpathconf(). Each names a limit or an
|
||||
* option of the FILESYSTEM holding the queried path or descriptor; see the
|
||||
* pathconf() documentation below for the supported keys and their values.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
_PC_LINK_MAX = 0, /* max link count of a file */
|
||||
_PC_MAX_CANON, /* max bytes in a terminal canonical input line */
|
||||
_PC_MAX_INPUT, /* max bytes available on a terminal input queue */
|
||||
_PC_NAME_MAX, /* max bytes in a file name (excluding NUL) */
|
||||
_PC_PATH_MAX, /* max bytes in a pathname (excluding NUL) */
|
||||
_PC_PIPE_BUF, /* max bytes atomically writable to a pipe */
|
||||
_PC_CHOWN_RESTRICTED, /* 1 if chown is restricted to the owner */
|
||||
_PC_NO_TRUNC, /* 1 if overlong file names are an error */
|
||||
_PC_VDISABLE, /* the terminal disable character (0 = none) */
|
||||
_PC_ASYNC_IO, /* 200809L if asynchronous I/O is supported */
|
||||
_PC_SYNC_IO, /* 200809L if synchronized I/O is supported */
|
||||
_PC_PRIO_IO, /* 200809L if prioritized I/O is supported */
|
||||
_PC_SOCK_MAXBUF, /* max bytes storable in a socket buffer */
|
||||
_PC_FILESIZEBITS, /* bits in a file size field */
|
||||
_PC_REC_INCR_XFER_SIZE, /* recommended increment for transfer sizes */
|
||||
_PC_REC_MAX_XFER_SIZE, /* recommended maximum transfer size */
|
||||
_PC_REC_MIN_XFER_SIZE, /* recommended minimum transfer size */
|
||||
_PC_REC_XFER_ALIGN, /* recommended transfer alignment */
|
||||
_PC_ALLOC_SIZE_MIN, /* minimum bytes of a file allocation */
|
||||
_PC_SYMLINK_MAX, /* max bytes in a symbolic link target */
|
||||
_PC_2_SYMLINKS /* 1 if symlinks support the ".." resolution rule */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Query a system configuration value for key name (one of the _SC_* above)
|
||||
* and return it as a long. A negative value means the option is not
|
||||
* supported or the key is unknown; for unsupported options and for any key
|
||||
* outside the _SC_* enum, -1 is returned WITHOUT setting errno (callers
|
||||
* must not rely on errno after -1).
|
||||
*
|
||||
* The supported keys and their vlibc values: _SC_ARG_MAX (2097152),
|
||||
* _SC_CHILD_MAX (65535), _SC_CLK_TCK (100, the fixed x86_64 clock-tick
|
||||
* rate — see the note in src/misc/sysconf.c, todo 35's times() consumes
|
||||
* that same constant), _SC_NGROUPS_MAX (65536), _SC_OPEN_MAX (the current
|
||||
* RLIMIT_NOFILE soft limit, 1024 if it cannot be read), _SC_PAGESIZE and
|
||||
* _SC_PAGE_SIZE (the memory page size from the AT_PAGESZ auxiliary vector
|
||||
* entry, 4096 on x86_64), _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN
|
||||
* (the processor count), _SC_PHYS_PAGES and _SC_AVPHYS_PAGES (physical and
|
||||
* available memory page counts), _SC_SYMLOOP_MAX (40), _SC_STREAM_MAX
|
||||
* (16), _SC_TZNAME_MAX (6), _SC_VERSION and _SC_2_VERSION (200809L),
|
||||
* _SC_JOB_CONTROL, _SC_SAVED_IDS, _SC_REGEXP, _SC_SHELL (1 each),
|
||||
* _SC_HOST_NAME_MAX (64), _SC_LOGIN_NAME_MAX (256), _SC_GETPW_R_SIZE_MAX
|
||||
* (1024), _SC_IOV_MAX (1024), _SC_LINE_MAX (2048), _SC_RE_DUP_MAX (32767),
|
||||
* _SC_COLL_WEIGHTS_MAX (255), _SC_EXPR_NEST_MAX (32), _SC_XOPEN_VERSION
|
||||
* (700), _SC_XOPEN_UNIX, _SC_XOPEN_CRYPT, _SC_XOPEN_ENH_I18N, _SC_XOPEN_SHM
|
||||
* (1 each), and the thread/option version keys (_SC_THREADS ... _SC_2_UPE,
|
||||
* 200809L each).
|
||||
*/
|
||||
long
|
||||
sysconf(int name);
|
||||
|
||||
/*
|
||||
* Query a configurable limit or option of the filesystem holding path (the
|
||||
* _PC_* keys above) and return it as a long; -1 with errno set when path
|
||||
* cannot be examined. Fixed values: _PC_LINK_MAX (32000), _PC_MAX_CANON
|
||||
* and _PC_MAX_INPUT (255), _PC_PATH_MAX (4096), _PC_PIPE_BUF (4096),
|
||||
* _PC_CHOWN_RESTRICTED, _PC_NO_TRUNC and _PC_2_SYMLINKS (1), _PC_VDISABLE
|
||||
* (0), _PC_FILESIZEBITS (64), and _PC_REC_MIN_XFER_SIZE,
|
||||
* _PC_REC_XFER_ALIGN and _PC_ALLOC_SIZE_MIN (the filesystem block size).
|
||||
* _PC_NAME_MAX is the filesystem's f_namelen (255 on ext4/xfs). For keys
|
||||
* whose option the filesystem does not provide (_PC_ASYNC_IO, _PC_SYNC_IO,
|
||||
* _PC_PRIO_IO, _PC_SOCK_MAXBUF, _PC_REC_INCR_XFER_SIZE,
|
||||
* _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX) and for any key outside the
|
||||
* _PC_* enum, -1 is returned WITHOUT setting errno.
|
||||
*/
|
||||
long
|
||||
pathconf(const char *path, int name);
|
||||
|
||||
/*
|
||||
* Like pathconf(), but the filesystem is the one holding the open
|
||||
* descriptor fildes.
|
||||
*/
|
||||
long
|
||||
fpathconf(int fildes, int name);
|
||||
|
||||
/* Level 1 (POSIX base): current directory, links, and path operations. */
|
||||
|
||||
/*
|
||||
* Store the absolute pathname of the current working directory into buf.
|
||||
* When buf is NULL, the path is returned in a malloc'd buffer instead
|
||||
* (size is then only a hint: 0 grows from a default of 128 bytes, and
|
||||
* the buffer is enlarged as the kernel reports ERANGE); the caller must
|
||||
* free it. Return buf (or the malloc'd buffer), or NULL with errno set
|
||||
* (EINVAL for a non-NULL buf with size 0).
|
||||
*/
|
||||
char *
|
||||
getcwd(char *buf, size_t size);
|
||||
|
||||
/*
|
||||
* Change the current working directory to path (chdir) or to the
|
||||
* directory referred to by the open descriptor fildes (fchdir). Return
|
||||
* 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
chdir(const char *path);
|
||||
|
||||
int
|
||||
fchdir(int fildes);
|
||||
|
||||
/*
|
||||
* Create a symbolic link: path2 is created as a link whose contents are
|
||||
* the string path1 (which need not exist). Return 0, or -1 with errno
|
||||
* set.
|
||||
*/
|
||||
int
|
||||
symlink(const char *path1, const char *path2);
|
||||
|
||||
/*
|
||||
* Like symlink(), but the link is created in the directory named by fd
|
||||
* when path2 is relative (use AT_FDCWD for the current working
|
||||
* directory). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
symlinkat(const char *path1, int fd, const char *path2);
|
||||
|
||||
/*
|
||||
* Read the contents of the symbolic link path into buf (bufsize bytes,
|
||||
* no NUL is appended) and return the number of bytes placed in buf, 0
|
||||
* for an empty target, or -1 with errno set. The buffer is not
|
||||
* terminated: callers sized for bufsize + 1 append the NUL themselves.
|
||||
*/
|
||||
ssize_t
|
||||
readlink(const char *restrict path, char *restrict buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* Like readlink(), but path is resolved in the directory named by fd
|
||||
* when relative. Return the number of bytes placed in buf, or -1 with
|
||||
* errno set.
|
||||
*/
|
||||
ssize_t
|
||||
readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize);
|
||||
|
||||
/*
|
||||
* Create a hard link: new is created as a second name for the file old
|
||||
* (which must not be a directory and must be on the same filesystem).
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
link(const char *old, const char *new);
|
||||
|
||||
/*
|
||||
* Like link(), but old and new are resolved against the directories
|
||||
* named by fd1 and fd2 when relative (AT_FDCWD selects the current
|
||||
* working directory) and flag may hold AT_SYMLINK_FOLLOW from <fcntl.h>.
|
||||
* Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
linkat(int fd1, const char *old, int fd2, const char *new, int flag);
|
||||
|
||||
/*
|
||||
* Remove the name path (a hard link; symbolic links are removed, not
|
||||
* followed). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
unlink(const char *path);
|
||||
|
||||
/*
|
||||
* Like unlink(), but path is resolved in the directory named by fd when
|
||||
* relative and flag may hold AT_REMOVEDIR from <fcntl.h> (making it
|
||||
* remove an empty directory instead). Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
unlinkat(int fd, const char *path, int flag);
|
||||
|
||||
/*
|
||||
* Remove the empty directory path. Return 0, or -1 with errno set
|
||||
* (ENOTEMPTY/EEXIST when the directory is not empty).
|
||||
*/
|
||||
int
|
||||
rmdir(const char *path);
|
||||
|
||||
/*
|
||||
* Rename old to new within the filesystem: old is resolved against fd1
|
||||
* and new against fd2 (AT_FDCWD for the current working directory). Both
|
||||
* names may be directories. Return 0, or -1 with errno set.
|
||||
*/
|
||||
int
|
||||
renameat(int fd1, const char *old, int fd2, const char *new);
|
||||
|
||||
/* Level 1 (POSIX base): terminal identity, login name, getopt, confstr. */
|
||||
|
||||
/*
|
||||
* Test whether fildes refers to a terminal. Return 1 when it does, and
|
||||
* 0 otherwise (errno may be set — e.g. EBADF — but the caller must not
|
||||
* rely on it after a 0 return).
|
||||
*/
|
||||
int
|
||||
isatty(int fildes);
|
||||
|
||||
/*
|
||||
* Return the pathname of the terminal open on fildes in a shared static
|
||||
* buffer (overwritten by the next call), or NULL with errno set when
|
||||
* fildes is not a terminal. ttyname_r stores the path in the caller's
|
||||
* name buffer instead and returns 0, or an error NUMBER (ENOTTY when
|
||||
* fildes is not a terminal, ERANGE when the name does not fit in
|
||||
* namesize).
|
||||
*/
|
||||
char *
|
||||
ttyname(int fildes);
|
||||
|
||||
int
|
||||
ttyname_r(int fildes, char *name, size_t namesize);
|
||||
|
||||
/*
|
||||
* Return the login name of the user associated with the calling process
|
||||
* (the owner of its controlling terminal) in a shared static buffer, or
|
||||
* NULL with errno set when the name cannot be determined. getlogin_r
|
||||
* stores the name in the caller's buffer and returns 0, or an error
|
||||
* number (ERANGE when the name does not fit in namesize).
|
||||
*/
|
||||
char *
|
||||
getlogin(void);
|
||||
|
||||
int
|
||||
getlogin_r(char *name, size_t namesize);
|
||||
|
||||
/*
|
||||
* Command-line option parsing (POSIX.1-2008). getopt returns the next
|
||||
* option character from argv per the option characters in optstring; a
|
||||
* character followed by ':' in optstring takes an argument (delivered in
|
||||
* optarg). A leading ':' in optstring makes missing-argument errors
|
||||
* return ':' silently instead of '?' with a diagnostic to stderr.
|
||||
* Return -1 when all options have been consumed: optind then points at
|
||||
* the first non-option argument, and "--" terminates the scan.
|
||||
*/
|
||||
int
|
||||
getopt(int argc, char *const argv[], const char *optstring);
|
||||
|
||||
/* The getopt option state (POSIX.1-2008 base). */
|
||||
extern char *optarg; /* the argument of the option just returned */
|
||||
extern int optind; /* index of the next argument to scan (init 1) */
|
||||
extern int opterr; /* nonzero: write diagnostics to stderr (init 1) */
|
||||
extern int optopt; /* the option character that caused an error */
|
||||
|
||||
/*
|
||||
* Query the implementation-defined string value of configuration name
|
||||
* (one of the _CS_* keys above). When buf is not NULL and len is large
|
||||
* enough, the value (NUL-terminated) is copied into buf; the return
|
||||
* value is the length of the value including the terminating NUL, so a
|
||||
* return greater than len means the buffer was too small and nothing was
|
||||
* copied. For an unknown name, 0 is returned and errno is set to EINVAL.
|
||||
*/
|
||||
size_t
|
||||
confstr(int name, char *buf, size_t len);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): Linux extensions + XSI + obsolescent. */
|
||||
|
||||
/*
|
||||
* Store the current working directory into pathname, which must hold at
|
||||
* least PATH_MAX bytes. Return pathname, or NULL with errno set.
|
||||
* Obsolescent: prefer getcwd().
|
||||
*/
|
||||
char *
|
||||
getwd(char *pathname);
|
||||
|
||||
/*
|
||||
* Change the process root directory to path (used with chdir by
|
||||
* chroot-style confinement). Requires privilege. Return 0, or -1 with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
int
|
||||
chroot(const char *path);
|
||||
|
||||
/*
|
||||
* Copy nbytes bytes from from to to, swapping each adjacent pair of
|
||||
* bytes (the odd byte of an odd count is not copied). Used for historic
|
||||
* byte-order conversion; returns nothing. XSI.
|
||||
*/
|
||||
void
|
||||
swab(const void *restrict from, void *restrict to, ssize_t nbytes);
|
||||
|
||||
/*
|
||||
* Like dup2(), but with descriptor flags (O_CLOEXEC from <fcntl.h>) applied
|
||||
* atomically; return fildes2, or -1 with errno set. Linux-specific.
|
||||
*/
|
||||
int
|
||||
dup3(int fildes, int fildes2, int flags);
|
||||
|
||||
/*
|
||||
* Like pipe(), but with descriptor flags (e.g. O_CLOEXEC) applied
|
||||
* atomically; return 0, or -1 with errno set. Linux-specific.
|
||||
*/
|
||||
int
|
||||
pipe2(int fildes[2], int flags);
|
||||
|
||||
/*
|
||||
* Truncate the file named by path to length bytes; return 0, or -1 with
|
||||
* errno set. XSI.
|
||||
*/
|
||||
int
|
||||
truncate(const char *path, off_t length);
|
||||
|
||||
/*
|
||||
* glibc LFS alias of lseek(): on x86_64 the LFS and non-LFS off_t are
|
||||
* identical (both 64-bit), so this simply calls lseek(). Provided for
|
||||
* source compatibility only.
|
||||
*/
|
||||
off_t
|
||||
lseek64(int fildes, off_t offset, int whence);
|
||||
|
||||
/*
|
||||
* vfork (obsolescent): like fork(), but the child borrows the parent's
|
||||
* address space until it execs or exits.
|
||||
*/
|
||||
pid_t
|
||||
vfork(void);
|
||||
|
||||
/*
|
||||
* setpgrp (obsolescent): setpgid(0, 0) — the caller becomes the leader
|
||||
* of its own process group.
|
||||
*/
|
||||
int
|
||||
setpgrp(void);
|
||||
|
||||
/*
|
||||
* setgroups (XSI): install the supplementary group list of gidsetsize
|
||||
* entries. Requires privilege.
|
||||
*/
|
||||
int
|
||||
setgroups(size_t gidsetsize, const gid_t *grouplist);
|
||||
|
||||
/*
|
||||
* getpgid/getsid (XSI): the process group / session of pid (0 = the
|
||||
* caller).
|
||||
*/
|
||||
pid_t
|
||||
getpgid(pid_t pid);
|
||||
|
||||
pid_t
|
||||
getsid(pid_t pid);
|
||||
|
||||
/*
|
||||
* Controlling-terminal foreground process group (XSI): tcgetpgrp reads
|
||||
* the foreground group of the terminal on fildes; tcsetpgrp makes pgid
|
||||
* the foreground group.
|
||||
*/
|
||||
pid_t
|
||||
tcgetpgrp(int fildes);
|
||||
|
||||
int
|
||||
tcsetpgrp(int fildes, pid_t pgid);
|
||||
|
||||
/* Level 2 (muslmimic): system identification (XSI / BSD, not POSIX base). */
|
||||
|
||||
/*
|
||||
* The _CS_* keys for confstr() (todo 38, which owns the function and its
|
||||
* string results). Values are vlibc-local ABI like the _SC_ and _PC_ keys
|
||||
* above; they are defined here now so todo 38 only implements confstr()
|
||||
* itself.
|
||||
*/
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _CS_ names */
|
||||
enum
|
||||
{
|
||||
_CS_PATH = 0, /* value of PATH (always a usable default) */
|
||||
_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, /* 1 if the V6 width-restricted envs exist */
|
||||
_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS, /* 1 if the V7 width-restricted envs exist */
|
||||
_CS_LFS_CFLAGS, /* cpp/cflags for large-file support */
|
||||
_CS_LFS_LDFLAGS, /* ld flags for large-file support */
|
||||
_CS_LFS_LIBS, /* libraries for large-file support */
|
||||
_CS_LFS_LINTFLAGS, /* lint flags for large-file support */
|
||||
_CS_LFS64_CFLAGS, /* cpp/cflags for large-file64 support */
|
||||
_CS_LFS64_LDFLAGS, /* ld flags for large-file64 support */
|
||||
_CS_LFS64_LIBS, /* libraries for large-file64 support */
|
||||
_CS_LFS64_LINTFLAGS, /* lint flags for large-file64 support */
|
||||
_CS_GNU_LIBC_VERSION, /* "glibc X.Y" (vlibc extension) */
|
||||
_CS_GNU_LIBPTHREAD_VERSION /* "NPTL X.Y" (vlibc extension) */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated host name of the current machine into name,
|
||||
* sized len bytes. Return 0 on success; if the name (including its NUL
|
||||
* terminator) does not fit in len bytes, nothing is written beyond the
|
||||
* fit and -1 is returned with errno set to ENAMETOOLONG. XSI.
|
||||
*/
|
||||
int
|
||||
gethostname(char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Set the kernel's host name from name (len bytes, no NUL required).
|
||||
* Requires privilege. Return 0, or -1 with errno set. BSD, not POSIX.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Return the 32-bit host identifier of the current machine. vlibc returns
|
||||
* 0 (musl's behavior): the historic value was derived from the host's
|
||||
* primary IP address via gethostbyname, which the modern library does not
|
||||
* perform. [OB] — obsolescent in POSIX.
|
||||
*/
|
||||
long
|
||||
gethostid(void);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_UNISTD_H */
|
||||
@@ -57,4 +57,21 @@
|
||||
#define VLIBC_HAS_VLIBC 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Per-header availability gates, derived from VLIBC_LEVEL. The ISO C core
|
||||
* headers (<stddef.h>, <stdint.h>, <stdbool.h>, <stdalign.h>, <iso646.h>,
|
||||
* <limits.h>, <float.h>, <assert.h>, <stdarg.h>, <stdnoreturn.h>,
|
||||
* <stdatomic.h>, <stdbit.h>, <stdckdint.h>, <tgmath.h>) are present in every
|
||||
* profile and carry no gate. The POSIX-facing headers below exist only in
|
||||
* profiles that provide POSIX (level >= 1, i.e. all profiles today); each
|
||||
* header includes this file and gates its declarations on its own macro.
|
||||
*/
|
||||
#if VLIBC_LEVEL >= 1
|
||||
#define VLIBC_HAS_HEADER_SYS_TYPES_H 1
|
||||
#define VLIBC_HAS_HEADER_UCHAR_H 1
|
||||
#define VLIBC_HAS_HEADER_CPIO_H 1
|
||||
#define VLIBC_HAS_HEADER_TAR_H 1
|
||||
#define VLIBC_HAS_HEADER_THREADS_H 1
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_FEATURES_H */
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
#ifndef VLIBC_TEST_H
|
||||
#define VLIBC_TEST_H
|
||||
|
||||
/*
|
||||
* vlibc — shared test contract (todo 6).
|
||||
*
|
||||
* Every future per-function test (test_*.c) includes this header and gets:
|
||||
*
|
||||
* - TEST_ASSERT_EQ(actual, expected) integer equality, compared as
|
||||
* signed 64-bit; on failure prints
|
||||
* both values in hex
|
||||
* - TEST_ASSERT_TRUE(cond) boolean check
|
||||
* - TEST_ASSERT_STREQ(actual, expected) NUL-terminated string equality;
|
||||
* NULL-safe (NULL equals only NULL)
|
||||
* - TEST_ASSERT_NULL(p) pointer is NULL
|
||||
* - TEST_MAIN(tests) a main() that runs the array of
|
||||
* struct vlibc_test entries, prints
|
||||
* one "RUN <name>: PASS|FAIL" line
|
||||
* per test and a PASS/FAIL summary,
|
||||
* and returns 0 when everything
|
||||
* passed and 1 otherwise
|
||||
*
|
||||
* Contract:
|
||||
* - Dependency-light: needs only vlibc's own <stddef.h> and the internal
|
||||
* raw-syscall layer (src/internal/syscall.h, via a path-relative
|
||||
* include). It deliberately includes NO host header and does NOT use
|
||||
* vlibc stdio (which does not exist yet). All output goes through raw
|
||||
* SYS_write to fd 1 (progress/summary) and fd 2 (assertion failures).
|
||||
* - Compile tests with -Iinclude -std=c23 -fno-stack-protector
|
||||
* -fno-pic -fno-pie (the make check harness does this; see Makefile.am)
|
||||
* and link statically against the vlibc test archive.
|
||||
* - A test function returns 0 on success, nonzero on failure; TEST_MAIN
|
||||
* aggregates both the return values and any assertion failures.
|
||||
* - The failure counter is per translation unit (one static int); keep
|
||||
* each test binary a single TU.
|
||||
* - Tests that need argc/argv (mode flags like -f) write their own
|
||||
* main() and may still use the TEST_ASSERT_* macros.
|
||||
* - One test function per checked behavior, named for the output.
|
||||
* - All macro arguments are evaluated exactly once.
|
||||
* - Do NOT include host headers in a TU that includes this file: the
|
||||
* -Iinclude path shadows GCC's internal headers (see tests/test_strerror.c).
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../../../src/internal/syscall.h"
|
||||
|
||||
/* One registered test: a name for the runner output and its run function. */
|
||||
struct vlibc_test
|
||||
{
|
||||
const char *name;
|
||||
int (*run)(void);
|
||||
};
|
||||
|
||||
/* Assertion failures recorded in this translation unit. */
|
||||
static int vlibc_test_failures;
|
||||
|
||||
/* ---- Raw output helpers (no stdio: raw SYS_write only) ---- */
|
||||
|
||||
static inline long
|
||||
vlibc_test_strlen(const char *s)
|
||||
{
|
||||
long n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline void
|
||||
vlibc_test_say(int fd, const char *s)
|
||||
{
|
||||
(void)__syscall3(SYS_write, fd, (long)s, vlibc_test_strlen(s));
|
||||
}
|
||||
|
||||
/*
|
||||
* The helpers below all take an (fd, value) or (value, name, file, line)
|
||||
* argument shape whose order is a fixed diagnostic convention; the
|
||||
* easily-swappable-parameters warning does not apply.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
static inline void
|
||||
vlibc_test_say_dec(int fd, unsigned long v)
|
||||
{
|
||||
char buf[24];
|
||||
int i = (int)sizeof(buf);
|
||||
|
||||
buf[--i] = '\0';
|
||||
do
|
||||
{
|
||||
buf[--i] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
} while (v != 0);
|
||||
(void)__syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i));
|
||||
}
|
||||
|
||||
static inline void
|
||||
vlibc_test_say_hex(int fd, unsigned long long v)
|
||||
{
|
||||
char buf[18];
|
||||
int i = (int)sizeof(buf);
|
||||
|
||||
buf[--i] = '\0';
|
||||
do
|
||||
{
|
||||
unsigned int d = (unsigned int)(v & 0xf);
|
||||
|
||||
buf[--i] = (char)(d < 10 ? '0' + d : 'a' + d - 10);
|
||||
v >>= 4;
|
||||
} while (v != 0);
|
||||
(void)__syscall3(SYS_write, fd, (long)(buf + i), (long)(sizeof(buf) - 1 - i));
|
||||
}
|
||||
|
||||
/* ---- Assertion machinery (implementation side of the macros) ---- */
|
||||
|
||||
static inline void
|
||||
vlibc_test_fail_header(const char *file, int line)
|
||||
{
|
||||
vlibc_test_failures++;
|
||||
vlibc_test_say(2, "ASSERT FAIL: ");
|
||||
vlibc_test_say(2, file);
|
||||
vlibc_test_say(2, ":");
|
||||
vlibc_test_say_dec(2, (unsigned long)line);
|
||||
vlibc_test_say(2, ": ");
|
||||
}
|
||||
|
||||
static inline void // NOLINT(bugprone-easily-swappable-parameters)
|
||||
vlibc_test_check(int ok, const char *what, const char *file, int line)
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
vlibc_test_fail_header(file, line);
|
||||
vlibc_test_say(2, what);
|
||||
vlibc_test_say(2, " is false\n");
|
||||
}
|
||||
|
||||
static inline void // NOLINT(bugprone-easily-swappable-parameters)
|
||||
vlibc_test_check_eq(long long actual, long long expected, const char *actual_s,
|
||||
const char *expected_s, const char *file, int line)
|
||||
{
|
||||
if (actual == expected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
vlibc_test_fail_header(file, line);
|
||||
vlibc_test_say(2, "TEST_ASSERT_EQ(");
|
||||
vlibc_test_say(2, actual_s);
|
||||
vlibc_test_say(2, ", ");
|
||||
vlibc_test_say(2, expected_s);
|
||||
vlibc_test_say(2, "): got 0x");
|
||||
vlibc_test_say_hex(2, (unsigned long long)actual);
|
||||
vlibc_test_say(2, ", want 0x");
|
||||
vlibc_test_say_hex(2, (unsigned long long)expected);
|
||||
vlibc_test_say(2, "\n");
|
||||
}
|
||||
|
||||
static inline int
|
||||
vlibc_test_str_eq(const char *a, const char *b)
|
||||
{
|
||||
if (a == b)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (a == NULL || b == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
while (*a != '\0' && *a == *b)
|
||||
{
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static inline void // NOLINT(bugprone-easily-swappable-parameters)
|
||||
vlibc_test_check_streq(const char *actual, const char *expected,
|
||||
const char *actual_s, const char *expected_s,
|
||||
const char *file, int line)
|
||||
{
|
||||
if (vlibc_test_str_eq(actual, expected))
|
||||
{
|
||||
return;
|
||||
}
|
||||
vlibc_test_fail_header(file, line);
|
||||
vlibc_test_say(2, "TEST_ASSERT_STREQ(");
|
||||
vlibc_test_say(2, actual_s);
|
||||
vlibc_test_say(2, ", ");
|
||||
vlibc_test_say(2, expected_s);
|
||||
vlibc_test_say(2, "): got \"");
|
||||
vlibc_test_say(2, actual == NULL ? "(null)" : actual);
|
||||
vlibc_test_say(2, "\", want \"");
|
||||
vlibc_test_say(2, expected == NULL ? "(null)" : expected);
|
||||
vlibc_test_say(2, "\"\n");
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
/* ---- Public assertion macros ---- */
|
||||
|
||||
#define TEST_ASSERT_TRUE(cond) \
|
||||
do \
|
||||
{ \
|
||||
vlibc_test_check((cond) != 0, #cond, __FILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define TEST_ASSERT_EQ(actual, expected) \
|
||||
do \
|
||||
{ \
|
||||
vlibc_test_check_eq((long long)(actual), (long long)(expected), \
|
||||
#actual, #expected, __FILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define TEST_ASSERT_NULL(p) \
|
||||
do \
|
||||
{ \
|
||||
vlibc_test_check((p) == NULL, #p " == NULL", __FILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
#define TEST_ASSERT_STREQ(actual, expected) \
|
||||
do \
|
||||
{ \
|
||||
vlibc_test_check_streq((actual), (expected), #actual, #expected, \
|
||||
__FILE__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
/* ---- Runner ---- */
|
||||
|
||||
#define TEST_MAIN(tests) \
|
||||
int \
|
||||
main(void) \
|
||||
{ \
|
||||
const size_t vlibc_test_count = sizeof(tests) / sizeof((tests)[0]); \
|
||||
size_t vlibc_test_i; \
|
||||
size_t vlibc_test_passed = 0; \
|
||||
for (vlibc_test_i = 0; vlibc_test_i < vlibc_test_count; vlibc_test_i++) \
|
||||
{ \
|
||||
int vlibc_test_before = vlibc_test_failures; \
|
||||
vlibc_test_say(1, "RUN "); \
|
||||
vlibc_test_say(1, (tests)[vlibc_test_i].name); \
|
||||
vlibc_test_say(1, ": "); \
|
||||
if ((tests)[vlibc_test_i].run() == 0 && \
|
||||
vlibc_test_failures == vlibc_test_before) \
|
||||
{ \
|
||||
vlibc_test_say(1, "PASS\n"); \
|
||||
vlibc_test_passed++; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
vlibc_test_say(1, "FAIL\n"); \
|
||||
} \
|
||||
} \
|
||||
vlibc_test_say(1, "SUMMARY: "); \
|
||||
vlibc_test_say_dec(1, (unsigned long)vlibc_test_passed); \
|
||||
vlibc_test_say(1, "/"); \
|
||||
vlibc_test_say_dec(1, (unsigned long)vlibc_test_count); \
|
||||
vlibc_test_say(1, " passed, "); \
|
||||
vlibc_test_say_dec(1, (unsigned long)vlibc_test_failures); \
|
||||
vlibc_test_say(1, " assertion failure(s)\n"); \
|
||||
return vlibc_test_passed == vlibc_test_count ? 0 : 1; \
|
||||
}
|
||||
|
||||
#endif /* VLIBC_TEST_H */
|
||||
@@ -0,0 +1,172 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* The "C" locale character-class table: one entry per unsigned char value,
|
||||
* holding the OR of the class bits below. The table is laid out as an
|
||||
* explicit 8-entries-per-row grid so each byte can be audited against the
|
||||
* ASCII ranges the standard specifies. The 0x80..0xff half is deliberately
|
||||
* not written: a static initializer zero-fills the elements it does not
|
||||
* mention, so every high byte — entry 255 included, which is where EOF
|
||||
* lands after the unsigned char cast — classifies false. Because every
|
||||
* lookup casts the argument to unsigned char first:
|
||||
*
|
||||
* - a negative char value can never index out of bounds;
|
||||
* - EOF classifies false in every table-driven is* function without a
|
||||
* special case;
|
||||
* - tolower/toupper pass EOF (and every non-letter) through unchanged,
|
||||
* returning the argument as given.
|
||||
*
|
||||
* isdigit, isxdigit and isprint are not table-driven: they are single
|
||||
* unsigned comparisons on the argument (see below), which makes them
|
||||
* genuinely const-capable — but only isdigit/isxdigit are declared const:
|
||||
* GCC predeclares isdigit/isxdigit as const builtins and isprint as a pure
|
||||
* builtin, and the header's attribute must match each builtin's declaration
|
||||
* or the weaker one is rejected with a warning.
|
||||
*/
|
||||
|
||||
#define CTYPE_UPPER 0x01U
|
||||
#define CTYPE_LOWER 0x02U
|
||||
#define CTYPE_DIGIT 0x04U
|
||||
#define CTYPE_CNTRL 0x08U
|
||||
#define CTYPE_PUNCT 0x10U
|
||||
#define CTYPE_SPACE 0x20U
|
||||
#define CTYPE_BLANK 0x80U
|
||||
|
||||
/* Combined masks, mirroring the standard's definitions. */
|
||||
#define CTYPE_ALPHA (CTYPE_UPPER | CTYPE_LOWER)
|
||||
#define CTYPE_ALNUM (CTYPE_ALPHA | CTYPE_DIGIT)
|
||||
#define CTYPE_GRAPH (CTYPE_ALNUM | CTYPE_PUNCT)
|
||||
|
||||
static const unsigned char ctype_class[256] = {
|
||||
/* 0 1 2 3 4 5 6 7 */
|
||||
/* 0x00 */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
/* 0x08 */ 0x08, 0xa8, 0x28, 0x28, 0x28, 0x28, 0x08, 0x08,
|
||||
/* 0x10 */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
/* 0x18 */ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
/* 0x20 */ 0xa0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x28 */ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x30 */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
|
||||
/* 0x38 */ 0x04, 0x04, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x40 */ 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
/* 0x48 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
/* 0x50 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||
/* 0x58 */ 0x01, 0x01, 0x01, 0x10, 0x10, 0x10, 0x10, 0x10,
|
||||
/* 0x60 */ 0x10, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
/* 0x68 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
/* 0x70 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
/* 0x78 */ 0x02, 0x02, 0x02, 0x10, 0x10, 0x10, 0x10, 0x08,
|
||||
/* 0x80..0xff stay zero by the static initializer's default. */
|
||||
};
|
||||
|
||||
int
|
||||
isalnum(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_ALNUM) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isalpha(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_ALPHA) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isblank(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_BLANK) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
iscntrl(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_CNTRL) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pure arithmetic (no table): the unsigned cast turns every value below '0'
|
||||
* into a huge number, so exactly 0..9 pass the range check.
|
||||
*/
|
||||
int
|
||||
isdigit(int c)
|
||||
{
|
||||
return (unsigned int)c - '0' < 10U;
|
||||
}
|
||||
|
||||
int
|
||||
isgraph(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_GRAPH) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
islower(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_LOWER) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pure arithmetic (no table): the printable range 0x20..0x7e.
|
||||
*/
|
||||
int
|
||||
isprint(int c)
|
||||
{
|
||||
return (unsigned int)c - 0x20 < 0x5fU;
|
||||
}
|
||||
|
||||
int
|
||||
ispunct(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_PUNCT) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isspace(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_SPACE) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
isupper(int c)
|
||||
{
|
||||
return (ctype_class[(unsigned char)c] & CTYPE_UPPER) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pure arithmetic (no table): c | 0x20 folds 'A'..'F' to 'a'..'f'; the
|
||||
* unsigned cast first keeps every negative value out of the range check.
|
||||
*/
|
||||
int
|
||||
isxdigit(int c)
|
||||
{
|
||||
return (unsigned int)c - '0' < 10U || (unsigned int)(c | 0x20) - 'a' < 6U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map an uppercase letter to lowercase and pass everything else — non-letters
|
||||
* and EOF alike — through unchanged. The class bit is authoritative, so the
|
||||
* unsigned char cast already keeps EOF and negative values out of the
|
||||
* mapping branch.
|
||||
*/
|
||||
int
|
||||
tolower(int c)
|
||||
{
|
||||
if ((ctype_class[(unsigned char)c] & CTYPE_UPPER) != 0)
|
||||
{
|
||||
return c + ('a' - 'A');
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
toupper(int c)
|
||||
{
|
||||
if ((ctype_class[(unsigned char)c] & CTYPE_LOWER) != 0)
|
||||
{
|
||||
return c - ('a' - 'A');
|
||||
}
|
||||
return c;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* True when c is a 7-bit ASCII value. EOF and every other negative value are
|
||||
* outside the range, so they classify false without a cast.
|
||||
*/
|
||||
int
|
||||
isascii(int c)
|
||||
{
|
||||
return c >= 0 && c < 128;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/*
|
||||
* Clear every bit above the low 7 of c. Pure bit arithmetic: defined for
|
||||
* every int value, EOF included (toascii(-1) is 0x7f).
|
||||
*/
|
||||
int
|
||||
toascii(int c)
|
||||
{
|
||||
return c & 0x7f;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* alphasort (todo 24, XSI): the scandir comparator that orders entries by
|
||||
* strcmp on their names. Receives two pointers to the array's struct
|
||||
* dirent pointers.
|
||||
*/
|
||||
int
|
||||
alphasort(const struct dirent **a, const struct dirent **b)
|
||||
{
|
||||
return strcmp((*a)->d_name, (*b)->d_name);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* closedir (todo 24): release the descriptor and the stream storage. The
|
||||
* descriptor is closed first so a close failure still reports -1, but the
|
||||
* stream is freed either way (its errno is preserved across the free).
|
||||
*/
|
||||
int
|
||||
closedir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
int fd = d->fd;
|
||||
int r = syscall_ret(__syscall1(SYS_close, fd));
|
||||
|
||||
__libc_free(d);
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef VLIBC_DIRENT_INTERNAL_H
|
||||
#define VLIBC_DIRENT_INTERNAL_H
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
* vlibc — internal directory-stream state (todo 24).
|
||||
*
|
||||
* struct vlibc_DIR is the completion of the opaque DIR handle from
|
||||
* <dirent.h>. readdir parses raw getdents64 records out of buf; the kernel
|
||||
* fills buf with whole records and advances its own per-fd directory
|
||||
* offset, so sequential iteration needs no lseek. seekdir/telldir hand the
|
||||
* kernel's per-record d_off cookie (the offset of the NEXT entry) back and
|
||||
* forth via lseek: d->de.d_off therefore always holds the resume point
|
||||
* after the entry most recently returned by readdir.
|
||||
*/
|
||||
#define VLIBC_DIRENT_BUFSZ 2048
|
||||
|
||||
struct vlibc_DIR
|
||||
{
|
||||
int fd; /* directory descriptor, owned by the stream */
|
||||
size_t buf_pos; /* next unparsed byte within buf */
|
||||
size_t buf_end; /* first unused byte of buf */
|
||||
struct dirent de; /* storage for the entry readdir returns */
|
||||
char buf[VLIBC_DIRENT_BUFSZ];
|
||||
};
|
||||
|
||||
/*
|
||||
* The kernel-side record produced by SYS_getdents64, transcribed as the
|
||||
* x86_64 linux_dirent64 layout: ino at 0, off at 8, reclen at 16, type at
|
||||
* 18, name at 19. The 256-byte name array makes the struct exactly the
|
||||
* size of a maximal record: 19 + (255-name + NUL) = 275, padded by struct
|
||||
* alignment to 280, which is also the largest d_reclen the kernel emits
|
||||
* (every record length is rounded up to a multiple of 8). readdir memcpy's
|
||||
* a whole record into this struct before reading the fields, so no
|
||||
* unaligned or aliasing access ever happens.
|
||||
*/
|
||||
struct vlibc_linux_dirent64
|
||||
{
|
||||
ino_t d_ino;
|
||||
off_t d_off;
|
||||
unsigned short d_reclen;
|
||||
unsigned char d_type;
|
||||
char d_name[256];
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct vlibc_linux_dirent64) == 280,
|
||||
"linux_dirent64 with a 256-byte name must be 280 bytes");
|
||||
_Static_assert(offsetof(struct vlibc_linux_dirent64, d_name) == 19,
|
||||
"linux_dirent64 name must sit at offset 19");
|
||||
_Static_assert(offsetof(struct dirent, d_name) ==
|
||||
offsetof(struct vlibc_linux_dirent64, d_name),
|
||||
"dirent and linux_dirent64 must share the name offset");
|
||||
|
||||
#endif /* VLIBC_DIRENT_INTERNAL_H */
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* dirfd (todo 24): return the descriptor the stream reads through. The
|
||||
* descriptor stays owned by the stream; closedir closes it.
|
||||
*/
|
||||
int
|
||||
dirfd(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
return d->fd;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* fdopendir (todo 24): validate the descriptor with SYS_fstat (POSIX
|
||||
* requires the fd to name a directory), then allocate the stream state.
|
||||
* On failure the descriptor is NOT closed — it stays owned by the caller,
|
||||
* exactly as passed in.
|
||||
*/
|
||||
DIR *
|
||||
fdopendir(int fd)
|
||||
{
|
||||
struct stat st;
|
||||
DIR *d;
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_fstat, fd, (long)&st)) < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return NULL;
|
||||
}
|
||||
d = __libc_malloc(sizeof *d);
|
||||
if (d == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
d->fd = fd;
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = 0;
|
||||
d->de.d_off = 0;
|
||||
return d;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* opendir (todo 24): open the directory via SYS_openat with O_DIRECTORY so
|
||||
* the kernel rejects non-directories with ENOTDIR before any stream state
|
||||
* exists, then hand the descriptor to fdopendir (which re-validates with
|
||||
* fstat). On fdopendir failure the descriptor is closed again: opendir
|
||||
* never leaks.
|
||||
*/
|
||||
DIR *
|
||||
opendir(const char *path)
|
||||
{
|
||||
int fd = syscall_ret(
|
||||
__syscall3(SYS_openat, AT_FDCWD, (long)path, O_RDONLY | O_DIRECTORY));
|
||||
DIR *d;
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
d = fdopendir(fd);
|
||||
if (d == NULL)
|
||||
{
|
||||
syscall_ret(__syscall1(SYS_close, fd));
|
||||
return NULL;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* readdir (todo 24): expose the entries the kernel wrote into the stream
|
||||
* buffer by SYS_getdents64, one per call. When the buffer is exhausted a
|
||||
* fresh getdents64 continues at the kernel's per-fd directory offset (no
|
||||
* lseek needed for sequential iteration). A zero-length result is end of
|
||||
* directory: NULL is returned with errno untouched. Each record is copied
|
||||
* into the stream's own struct dirent, so the returned pointer stays valid
|
||||
* until the next call on the same stream.
|
||||
*/
|
||||
struct dirent *
|
||||
readdir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
struct vlibc_linux_dirent64 k;
|
||||
unsigned short reclen;
|
||||
size_t namelen;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (d->buf_pos >= d->buf_end)
|
||||
{
|
||||
long r =
|
||||
__syscall3(SYS_getdents64, d->fd, (long)d->buf,
|
||||
(long)sizeof(d->buf));
|
||||
|
||||
if (r <= 0)
|
||||
{
|
||||
syscall_ret(r);
|
||||
return NULL;
|
||||
}
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = (size_t)r;
|
||||
}
|
||||
|
||||
if (d->buf_end - d->buf_pos < offsetof(struct vlibc_linux_dirent64, d_name))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&reclen,
|
||||
d->buf + d->buf_pos + offsetof(struct vlibc_linux_dirent64, d_reclen),
|
||||
sizeof reclen);
|
||||
if (reclen < offsetof(struct vlibc_linux_dirent64, d_name) + 2 ||
|
||||
reclen > sizeof k)
|
||||
{
|
||||
/* The kernel always emits whole well-formed records; a broken
|
||||
* length would otherwise walk off the buffer. Stop the scan. */
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&k, d->buf + d->buf_pos, reclen);
|
||||
d->buf_pos += reclen;
|
||||
|
||||
d->de.d_ino = k.d_ino;
|
||||
d->de.d_off = k.d_off;
|
||||
d->de.d_reclen = reclen;
|
||||
d->de.d_type = k.d_type;
|
||||
namelen = (size_t)reclen - offsetof(struct vlibc_linux_dirent64, d_name);
|
||||
if (namelen >= sizeof(d->de.d_name))
|
||||
{
|
||||
namelen = sizeof(d->de.d_name) - 1;
|
||||
}
|
||||
memcpy(d->de.d_name, k.d_name, namelen);
|
||||
d->de.d_name[namelen] = '\0';
|
||||
return &d->de;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* readdir_r (todo 24, obsolescent): reentrant readdir. The result is
|
||||
* copied into the caller's buf and *result points at it; at end of
|
||||
* directory *result is NULL. Errors are reported as the return value
|
||||
* (never through errno), and errno is preserved across successful calls.
|
||||
* readdir_r is distinguished from end-of-directory by checking whether
|
||||
* readdir moved errno.
|
||||
*/
|
||||
int
|
||||
readdir_r(DIR *restrict dir, struct dirent *restrict buf,
|
||||
struct dirent **restrict result)
|
||||
{
|
||||
struct dirent *de;
|
||||
int saved = errno;
|
||||
|
||||
errno = 0;
|
||||
de = readdir(dir);
|
||||
if (de != NULL)
|
||||
{
|
||||
memcpy(buf, de, sizeof *buf);
|
||||
*result = buf;
|
||||
errno = saved;
|
||||
return 0;
|
||||
}
|
||||
if (errno != 0)
|
||||
{
|
||||
return errno;
|
||||
}
|
||||
errno = saved;
|
||||
*result = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* rewinddir (todo 24): return the stream to the start of the directory.
|
||||
* SYS_lseek back to offset 0 resets the kernel's per-fd directory
|
||||
* position; the cached records (if any) are dropped and the stored seek
|
||||
* cookie is reset, so the next readdir refills from the first entry.
|
||||
* rewinddir has no error return; a failed lseek leaves the stream
|
||||
* positioned wherever the kernel is.
|
||||
*/
|
||||
void
|
||||
rewinddir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
syscall_ret(__syscall3(SYS_lseek, d->fd, 0, SEEK_SET));
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = 0;
|
||||
d->de.d_off = 0;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* scandir (todo 24, XSI): walk the whole directory and hand back a sorted,
|
||||
* NULL-terminated array of heap copies. Entries are copied into
|
||||
* minimal-size blocks (header fields plus the name), all released with the
|
||||
* public free(); the array itself is a malloc'd growable buffer resized
|
||||
* with realloc. "." and ".." are reported like any other entry.
|
||||
*/
|
||||
|
||||
/* Copy one entry into minimal heap storage sized for its name. */
|
||||
static struct dirent *
|
||||
dirent_dup(const struct dirent *de)
|
||||
{
|
||||
size_t nlen = strlen(de->d_name) + 1;
|
||||
struct dirent *nd =
|
||||
__libc_malloc(offsetof(struct dirent, d_name) + nlen);
|
||||
|
||||
if (nd == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
memcpy(nd, de, offsetof(struct dirent, d_name));
|
||||
memcpy(nd->d_name, de->d_name, nlen);
|
||||
return nd;
|
||||
}
|
||||
|
||||
int
|
||||
scandir(const char *path, struct dirent ***res,
|
||||
int (*sel)(const struct dirent *),
|
||||
int (*cmp)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *de;
|
||||
struct dirent **names = NULL;
|
||||
size_t cnt = 0;
|
||||
size_t cap = 0;
|
||||
|
||||
d = opendir(path);
|
||||
if (d == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while ((de = readdir(d)) != NULL)
|
||||
{
|
||||
if (sel != NULL && !sel(de))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (cnt == cap)
|
||||
{
|
||||
size_t ncap = cap == 0 ? 8 : cap * 2;
|
||||
|
||||
if (ncap <= cap || ncap > (size_t)-1 / sizeof *names)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
names = realloc(names, ncap * sizeof *names);
|
||||
if (names == NULL)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
cap = ncap;
|
||||
}
|
||||
names[cnt] = dirent_dup(de);
|
||||
if (names[cnt] == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
if (cmp != NULL)
|
||||
{
|
||||
qsort(names, cnt, sizeof *names,
|
||||
(int (*)(const void *, const void *))cmp);
|
||||
}
|
||||
names = realloc(names, (cnt + 1) * sizeof *names);
|
||||
if (names == NULL)
|
||||
{
|
||||
goto fail_nofree;
|
||||
}
|
||||
names[cnt] = NULL;
|
||||
*res = names;
|
||||
return (int)cnt;
|
||||
|
||||
fail:
|
||||
while (cnt > 0)
|
||||
{
|
||||
__libc_free(names[--cnt]);
|
||||
}
|
||||
__libc_free(names);
|
||||
closedir(d);
|
||||
return -1;
|
||||
|
||||
fail_nofree:
|
||||
while (cnt > 0)
|
||||
{
|
||||
__libc_free(names[--cnt]);
|
||||
}
|
||||
__libc_free(names);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* seekdir (todo 24): reposition the stream to the directory location loc
|
||||
* (a d_off cookie previously returned by telldir). SYS_lseek to that
|
||||
* cookie makes the next SYS_getdents64 resume at the entry the cookie
|
||||
* denotes; any records cached ahead of the seek point are dropped. The
|
||||
* kernel accepts exactly the cookies getdents64 hands out, which is all
|
||||
* telldir ever returns. seekdir has no error return; on a failed lseek
|
||||
* the buffered records are kept so reading continues where it left off.
|
||||
*/
|
||||
void
|
||||
seekdir(DIR *dir, long loc)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
if (syscall_ret(__syscall3(SYS_lseek, d->fd, (long)loc, SEEK_SET)) >= 0)
|
||||
{
|
||||
d->buf_pos = 0;
|
||||
d->buf_end = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include "dirent_impl.h"
|
||||
|
||||
/*
|
||||
* telldir (todo 24): return the stream's current directory location.
|
||||
* readdir stores the kernel's d_off cookie of every entry it hands out —
|
||||
* the offset at which iteration resumes AFTER that entry — so the cookie
|
||||
* of the most recently returned entry is exactly the location a matching
|
||||
* seekdir must restore. A fresh or rewound stream reports 0, the start.
|
||||
*/
|
||||
long
|
||||
telldir(DIR *dir)
|
||||
{
|
||||
struct vlibc_DIR *d = dir;
|
||||
|
||||
return (long)d->de.d_off;
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* vlibc — strerror / strerror_r (todo 2).
|
||||
*
|
||||
* strerror maps an errno value to an informative, descriptive message. The
|
||||
* messages are this libc's own standard English texts (they need not match
|
||||
* glibc verbatim). Known values resolve to immutable static string constants;
|
||||
* unknown values resolve to "Unknown error <N>" with <N> formatted in place.
|
||||
*
|
||||
* Thread-safety of the unknown-value path: the formatted string lives in a
|
||||
* single static buffer, so two threads formatting *unknown* errno values
|
||||
* concurrently can observe each other's text. This tradeoff is deliberate at
|
||||
* this stage: unknown errno values are a rare diagnostic path, the library
|
||||
* has no per-thread storage beyond the errno TCB slot yet, and strerror must
|
||||
* not depend on malloc (or any other subsystem). The pointer returned for
|
||||
* known values is unaffected. Once the real TCB layout lands (startup todo),
|
||||
* this can migrate to a per-thread buffer without changing the interface.
|
||||
*
|
||||
* The unknown-value formatter is hand-rolled: strerror is libc base and must
|
||||
* not pull in stdio/string before those exist.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Message table: one entry per defined E* value, indexed directly by the
|
||||
* errno number. Designated initializers keep name/value pairs provably in
|
||||
* sync with <errno.h>; entries for the ABI gaps (41, 58 — values the kernel
|
||||
* never produces) stay NULL and are treated as unknown. EWOULDBLOCK and
|
||||
* EDEADLOCK are aliases of EAGAIN and EDEADLK, so they share those entries.
|
||||
* The array auto-sizes to EHWPOISON + 1 (134) entries.
|
||||
*/
|
||||
static const char *const errmsg[] = {
|
||||
[0] = "Success",
|
||||
[EPERM] = "Operation not permitted",
|
||||
[ENOENT] = "No such file or directory",
|
||||
[ESRCH] = "No such process",
|
||||
[EINTR] = "Interrupted system call",
|
||||
[EIO] = "Input/output error",
|
||||
[ENXIO] = "No such device or address",
|
||||
[E2BIG] = "Argument list too long",
|
||||
[ENOEXEC] = "Exec format error",
|
||||
[EBADF] = "Bad file descriptor",
|
||||
[ECHILD] = "No child processes",
|
||||
[EAGAIN] = "Resource temporarily unavailable",
|
||||
[ENOMEM] = "Cannot allocate memory",
|
||||
[EACCES] = "Permission denied",
|
||||
[EFAULT] = "Bad address",
|
||||
[ENOTBLK] = "Block device required",
|
||||
[EBUSY] = "Device or resource busy",
|
||||
[EEXIST] = "File exists",
|
||||
[EXDEV] = "Invalid cross-device link",
|
||||
[ENODEV] = "No such device",
|
||||
[ENOTDIR] = "Not a directory",
|
||||
[EISDIR] = "Is a directory",
|
||||
[EINVAL] = "Invalid argument",
|
||||
[ENFILE] = "Too many open files in system",
|
||||
[EMFILE] = "Too many open files",
|
||||
[ENOTTY] = "Inappropriate ioctl for device",
|
||||
[ETXTBSY] = "Text file busy",
|
||||
[EFBIG] = "File too large",
|
||||
[ENOSPC] = "No space left on device",
|
||||
[ESPIPE] = "Illegal seek",
|
||||
[EROFS] = "Read-only file system",
|
||||
[EMLINK] = "Too many links",
|
||||
[EPIPE] = "Broken pipe",
|
||||
[EDOM] = "Numerical argument out of domain",
|
||||
[ERANGE] = "Numerical result out of range",
|
||||
[EDEADLK] = "Resource deadlock would occur",
|
||||
[ENAMETOOLONG] = "File name too long",
|
||||
[ENOLCK] = "No locks available",
|
||||
[ENOSYS] = "Function not implemented",
|
||||
[ENOTEMPTY] = "Directory not empty",
|
||||
[ELOOP] = "Too many levels of symbolic links",
|
||||
[ENOMSG] = "No message of desired type",
|
||||
[EIDRM] = "Identifier removed",
|
||||
[ECHRNG] = "Channel number out of range",
|
||||
[EL2NSYNC] = "Level 2 not synchronized",
|
||||
[EL3HLT] = "Level 3 halted",
|
||||
[EL3RST] = "Level 3 reset",
|
||||
[ELNRNG] = "Link number out of range",
|
||||
[EUNATCH] = "Protocol driver not attached",
|
||||
[ENOCSI] = "No CSI structure available",
|
||||
[EL2HLT] = "Level 2 halted",
|
||||
[EBADE] = "Invalid exchange",
|
||||
[EBADR] = "Invalid request descriptor",
|
||||
[EXFULL] = "Exchange full",
|
||||
[ENOANO] = "No anode",
|
||||
[EBADRQC] = "Invalid request code",
|
||||
[EBADSLT] = "Invalid slot",
|
||||
[EBFONT] = "Bad font file format",
|
||||
[ENOSTR] = "Device not a stream",
|
||||
[ENODATA] = "No data available",
|
||||
[ETIME] = "Timer expired",
|
||||
[ENOSR] = "Out of streams resources",
|
||||
[ENONET] = "Machine is not on the network",
|
||||
[ENOPKG] = "Package not installed",
|
||||
[EREMOTE] = "Object is remote",
|
||||
[ENOLINK] = "Link has been severed",
|
||||
[EADV] = "Advertise error",
|
||||
[ESRMNT] = "Srmount error",
|
||||
[ECOMM] = "Communication error on send",
|
||||
[EPROTO] = "Protocol error",
|
||||
[EMULTIHOP] = "Multihop attempted",
|
||||
[EDOTDOT] = "RFS specific error",
|
||||
[EBADMSG] = "Bad message",
|
||||
[EOVERFLOW] = "Value too large for defined data type",
|
||||
[ENOTUNIQ] = "Name not unique on network",
|
||||
[EBADFD] = "File descriptor in bad state",
|
||||
[EREMCHG] = "Remote address changed",
|
||||
[ELIBACC] = "Can not access a needed shared library",
|
||||
[ELIBBAD] = "Accessing a corrupted shared library",
|
||||
[ELIBSCN] = ".lib section in a.out corrupted",
|
||||
[ELIBMAX] = "Attempting to link in too many shared libraries",
|
||||
[ELIBEXEC] = "Cannot exec a shared library directly",
|
||||
[EILSEQ] = "Invalid or incomplete multibyte or wide character",
|
||||
[ERESTART] = "Interrupted system call should be restarted",
|
||||
[ESTRPIPE] = "Streams pipe error",
|
||||
[EUSERS] = "Too many users",
|
||||
[ENOTSOCK] = "Socket operation on non-socket",
|
||||
[EDESTADDRREQ] = "Destination address required",
|
||||
[EMSGSIZE] = "Message too long",
|
||||
[EPROTOTYPE] = "Protocol wrong type for socket",
|
||||
[ENOPROTOOPT] = "Protocol not available",
|
||||
[EPROTONOSUPPORT] = "Protocol not supported",
|
||||
[ESOCKTNOSUPPORT] = "Socket type not supported",
|
||||
[EOPNOTSUPP] = "Operation not supported",
|
||||
[EPFNOSUPPORT] = "Protocol family not supported",
|
||||
[EAFNOSUPPORT] = "Address family not supported by protocol",
|
||||
[EADDRINUSE] = "Address already in use",
|
||||
[EADDRNOTAVAIL] = "Cannot assign requested address",
|
||||
[ENETDOWN] = "Network is down",
|
||||
[ENETUNREACH] = "Network is unreachable",
|
||||
[ENETRESET] = "Network dropped connection on reset",
|
||||
[ECONNABORTED] = "Software caused connection abort",
|
||||
[ECONNRESET] = "Connection reset by peer",
|
||||
[ENOBUFS] = "No buffer space available",
|
||||
[EISCONN] = "Transport endpoint is already connected",
|
||||
[ENOTCONN] = "Transport endpoint is not connected",
|
||||
[ESHUTDOWN] = "Cannot send after transport endpoint shutdown",
|
||||
[ETOOMANYREFS] = "Too many references: cannot splice",
|
||||
[ETIMEDOUT] = "Connection timed out",
|
||||
[ECONNREFUSED] = "Connection refused",
|
||||
[EHOSTDOWN] = "Host is down",
|
||||
[EHOSTUNREACH] = "No route to host",
|
||||
[EALREADY] = "Operation already in progress",
|
||||
[EINPROGRESS] = "Operation now in progress",
|
||||
[ESTALE] = "Stale file handle",
|
||||
[EUCLEAN] = "Structure needs cleaning",
|
||||
[ENOTNAM] = "Not a XENIX named type file",
|
||||
[ENAVAIL] = "No XENIX semaphores available",
|
||||
[EISNAM] = "Is a named type file",
|
||||
[EREMOTEIO] = "Remote I/O error",
|
||||
[EDQUOT] = "Disk quota exceeded",
|
||||
[ENOMEDIUM] = "No medium found",
|
||||
[EMEDIUMTYPE] = "Wrong medium type",
|
||||
[ECANCELED] = "Operation canceled",
|
||||
[ENOKEY] = "Required key not available",
|
||||
[EKEYEXPIRED] = "Key has expired",
|
||||
[EKEYREVOKED] = "Key has been revoked",
|
||||
[EKEYREJECTED] = "Key was rejected by service",
|
||||
[EOWNERDEAD] = "Owner died",
|
||||
[ENOTRECOVERABLE] = "State not recoverable",
|
||||
[ERFKILL] = "Operation not possible due to RF-kill",
|
||||
[EHWPOISON] = "Memory page has hardware error",
|
||||
};
|
||||
|
||||
/* The table spans 0..EHWPOISON exactly: the ABI gaps stay NULL entries. */
|
||||
_Static_assert(sizeof errmsg / sizeof errmsg[0] == EHWPOISON + 1,
|
||||
"strerror table must cover every E* value 0..EHWPOISON");
|
||||
|
||||
/* Shared storage for the unknown-value path (see the file-top comment). */
|
||||
static char errbuf[32];
|
||||
|
||||
/*
|
||||
* Write "Unknown error <errnum>" into dst, truncating to cap bytes and always
|
||||
* NUL-terminating when cap > 0. Hand-rolled so this file needs no stdio.
|
||||
*/
|
||||
static void
|
||||
format_unknown(int errnum, char *dst, size_t cap)
|
||||
{
|
||||
static const char prefix[] = "Unknown error ";
|
||||
char digits[12]; /* enough for "-2147483648" */
|
||||
size_t ndigits;
|
||||
size_t i;
|
||||
unsigned long mag;
|
||||
|
||||
if (cap == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Absolute value as unsigned long; INT_MIN negates safely in long. */
|
||||
mag = (unsigned long)(errnum < 0 ? -(long)errnum : errnum);
|
||||
|
||||
/* Digits, least significant first. */
|
||||
ndigits = 0;
|
||||
do
|
||||
{
|
||||
digits[ndigits] = (char)('0' + (int)(mag % 10));
|
||||
ndigits++;
|
||||
mag /= 10;
|
||||
} while (mag != 0);
|
||||
|
||||
i = 0;
|
||||
while (i + 1 < cap && prefix[i] != '\0')
|
||||
{
|
||||
dst[i] = prefix[i];
|
||||
i++;
|
||||
}
|
||||
if (i + 1 < cap && errnum < 0)
|
||||
{
|
||||
dst[i] = '-';
|
||||
i++;
|
||||
}
|
||||
while (i + 1 < cap && ndigits > 0)
|
||||
{
|
||||
ndigits--;
|
||||
dst[i] = digits[ndigits];
|
||||
i++;
|
||||
}
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the message for errnum, or NULL when it is unknown (out of table
|
||||
* range or an ABI gap).
|
||||
*/
|
||||
static const char *
|
||||
lookup(int errnum)
|
||||
{
|
||||
if (errnum >= 0 && errnum < (int)(sizeof errmsg / sizeof errmsg[0]))
|
||||
{
|
||||
return errmsg[errnum];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
strerror(int errnum)
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
msg = lookup(errnum);
|
||||
if (msg == NULL)
|
||||
{
|
||||
format_unknown(errnum, errbuf, sizeof errbuf);
|
||||
msg = errbuf;
|
||||
}
|
||||
return (char *)msg;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/*
|
||||
* XSI strerror_r: copy the message for errnum into buf (truncated to buflen,
|
||||
* always NUL-terminated when buflen > 0) and return 0. Unknown errnum values
|
||||
* produce "Unknown error <errnum>" with the same success return; buf == NULL
|
||||
* with buflen > 0 yields EINVAL; buflen == 0 writes nothing and returns 0.
|
||||
* The GNU char*-returning semantics are deliberately not implemented.
|
||||
*/
|
||||
int
|
||||
strerror_r(int errnum, char *buf, size_t buflen)
|
||||
{
|
||||
const char *msg;
|
||||
char unknown[32];
|
||||
size_t i;
|
||||
|
||||
if (buflen == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (buf == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
msg = lookup(errnum);
|
||||
if (msg == NULL)
|
||||
{
|
||||
format_unknown(errnum, unknown, sizeof unknown);
|
||||
msg = unknown;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (i + 1 < buflen && msg[i] != '\0')
|
||||
{
|
||||
buf[i] = msg[i];
|
||||
i++;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* creat ≡ open(path, O_WRONLY | O_CREAT | O_TRUNC, mode) (POSIX). Done as
|
||||
* a direct SYS_openat rather than a call to open(): the flag set is fixed,
|
||||
* the mode is always supplied, and this keeps src/fcntl self-contained
|
||||
* with no inter-object dependency on src/unistd. The kernel applies the
|
||||
* process umask to mode.
|
||||
*/
|
||||
int
|
||||
creat(const char *path, mode_t mode)
|
||||
{
|
||||
return syscall_ret(
|
||||
__syscall4(SYS_openat, AT_FDCWD, (long)path, O_WRONLY | O_CREAT | O_TRUNC, mode));
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* fcntl over SYS_fcntl (72). The third argument is read from the varargs
|
||||
* only for the commands POSIX defines one for (F_DUPFD, F_DUPFD_CLOEXEC,
|
||||
* F_SETFD, F_SETFL, the lock commands, and the owner/signal commands);
|
||||
* every other command passes 0, which the kernel ignores. Pointers (the
|
||||
* struct flock * of F_GETLK/F_SETLK/F_SETLKW/F_OFD_*) ride the varargs
|
||||
* slot as a long — on x86_64 long and void * share one GPR slot, so the
|
||||
* read is ABI-exact.
|
||||
*
|
||||
* F_DUPFD_CLOEXEC passes straight through: the x86_64 kernel has supported
|
||||
* it as a single native operation since 2.6.24, so no F_DUPFD + F_SETFD
|
||||
* fallback is needed. F_SETFL needs no O_LARGEFILE massaging on x86_64
|
||||
* (that is a 32-bit compat concern only).
|
||||
*/
|
||||
int
|
||||
fcntl(int fildes, int cmd, ...)
|
||||
{
|
||||
long arg = 0;
|
||||
va_list ap;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC:
|
||||
case F_SETFD:
|
||||
case F_SETFL:
|
||||
case F_GETLK:
|
||||
case F_SETLK:
|
||||
case F_SETLKW:
|
||||
case F_SETOWN:
|
||||
case F_SETSIG:
|
||||
case F_GETOWN_EX:
|
||||
case F_SETOWN_EX:
|
||||
case F_OFD_GETLK:
|
||||
case F_OFD_SETLK:
|
||||
case F_OFD_SETLKW:
|
||||
va_start(ap, cmd);
|
||||
arg = va_arg(ap, long);
|
||||
va_end(ap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return syscall_ret(__syscall3(SYS_fcntl, fildes, cmd, arg));
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* lockf (XSI) over the fcntl record-lock commands: the kernel has no lockf
|
||||
* syscall, so every cmd maps to a struct flock operation on the range
|
||||
* [current offset, current offset + len) — the POSIX definition. len 0
|
||||
* means through EOF, which the kernel's l_len 0 convention already
|
||||
* expresses.
|
||||
*
|
||||
* The region start uses l_whence = SEEK_CUR with l_start = 0: the kernel
|
||||
* resolves the current file offset at syscall time, atomically with the
|
||||
* lock operation, so no lseek round-trip (and no TOCTOU window) is needed.
|
||||
*
|
||||
* F_TEST probes with F_GETLK using a read lock (the probe type that
|
||||
* conflicts with any exclusive lock): if the kernel reports no lock, or
|
||||
* the reported holder is this process itself (a different descriptor of
|
||||
* ours may hold it — POSIX says our own process never conflicts with
|
||||
* itself), the region is lockable. The pid is read via raw SYS_getpid;
|
||||
* the process wrappers are todo 20's and not a dependency of this file.
|
||||
*/
|
||||
int
|
||||
lockf(int fd, int cmd, off_t len)
|
||||
{
|
||||
struct flock lk;
|
||||
|
||||
lk.l_type = F_WRLCK;
|
||||
lk.l_whence = SEEK_CUR;
|
||||
lk.l_start = 0;
|
||||
lk.l_len = len;
|
||||
|
||||
switch (cmd)
|
||||
{
|
||||
case F_TEST:
|
||||
lk.l_type = F_RDLCK;
|
||||
if (fcntl(fd, F_GETLK, &lk) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (lk.l_type == F_UNLCK || lk.l_pid == (pid_t)__syscall0(SYS_getpid))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
case F_ULOCK:
|
||||
lk.l_type = F_UNLCK;
|
||||
return fcntl(fd, F_SETLK, &lk);
|
||||
case F_LOCK:
|
||||
return fcntl(fd, F_SETLKW, &lk);
|
||||
case F_TLOCK:
|
||||
return fcntl(fd, F_SETLK, &lk);
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* posix_fadvise over SYS_fadvise64 (221). POSIX return convention, unlike
|
||||
* the rest of the family: an error NUMBER directly — 0 on success, else
|
||||
* the positive errno value (EBADF, ESPIPE, EINVAL, ...) — and errno is
|
||||
* left untouched. The raw syscall result is therefore mapped without the
|
||||
* syscall_ret() translation (which would write errno): a negative return
|
||||
* is negated, 0 stays 0. On x86_64 the syscall takes the offset as one
|
||||
* 64-bit value (fd, offset, len, advice) — no lo/hi split.
|
||||
*/
|
||||
int
|
||||
posix_fadvise(int fd, off_t offset, off_t len, int advice)
|
||||
{
|
||||
long r = __syscall4(SYS_fadvise64, fd, offset, len, advice);
|
||||
|
||||
return r < 0 ? (int)-r : 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* posix_fallocate over SYS_fallocate (285). The kernel signature is
|
||||
* (fd, mode, offset, len); mode is 0 — POSIX exposes no FALLOC_FL_* flags,
|
||||
* so the plain allocate-and-grow operation is all there is (the kernel
|
||||
* rejects any other mode with EOPNOTSUPP/EINVAL, which is exactly what
|
||||
* POSIX wants). Same error-number return convention as posix_fadvise:
|
||||
* 0 on success, the positive errno value on failure, errno untouched —
|
||||
* so the raw result is negated directly, no syscall_ret().
|
||||
*/
|
||||
int
|
||||
posix_fallocate(int fd, off_t offset, off_t len)
|
||||
{
|
||||
long r = __syscall4(SYS_fallocate, fd, 0, offset, len);
|
||||
|
||||
return r < 0 ? (int)-r : 0;
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <grp.h>
|
||||
|
||||
/*
|
||||
* vlibc — group database (todo 37).
|
||||
*
|
||||
* getgrnam/getgrgid scan /etc/group with a fresh fopen per call (no
|
||||
* persistent cursor); getgrent walks one FILE kept open between calls,
|
||||
* which setgrent rewinds and endgrent closes. Every line is read with
|
||||
* getline, one at a time — the whole file is never cached. fgetgrent is
|
||||
* the XSI variant that parses the next record from an arbitrary caller-
|
||||
* supplied stream; it is gated at level 2 (whole-level-gate rule: XSI is
|
||||
* not POSIX.1-2008 base), everything else here is level 1.
|
||||
*
|
||||
* Storage model: two static buffers back every non-reentrant result. A
|
||||
* successful call returns a struct whose string members and gr_mem
|
||||
* entries point into a private result copy; the getline scan that found
|
||||
* the match ran in a separate buffer, so a caller may feed the returned
|
||||
* record's own name back into a later lookup (the
|
||||
* getgrnam(getgrgid(x)->gr_name) round-trip) without the scan clobbering
|
||||
* its key. The result copy is replaced by the next call to any of
|
||||
* getgrnam/getgrgid/getgrent/fgetgrent (or a realloc of the copy),
|
||||
* matching POSIX result-lifetime rules. The _r forms never use this
|
||||
* static storage for their result: the caller's struct and buffer
|
||||
* receive the record, and the caller's *result is how the outcome is
|
||||
* reported.
|
||||
*
|
||||
* Record syntax: name:passwd:gid:member,member,... — the first three
|
||||
* fields are ':'-separated and the member list is the comma-separated
|
||||
* tail (empty when the group has no members). Blank or comment-style
|
||||
* lines and records whose gid is not a decimal number are skipped like
|
||||
* malformed input.
|
||||
*/
|
||||
|
||||
/* The database path; not overridable (no NSS, no shadow, POSIX only). */
|
||||
#define GRP_PATH "/etc/group"
|
||||
|
||||
/* Non-reentrant result: struct, the private copy it points into, and the
|
||||
* member pointer array whose entries point into that same copy. */
|
||||
static struct group gr_result;
|
||||
static char *gr_out;
|
||||
static size_t gr_out_cap;
|
||||
static char **gr_mem;
|
||||
static size_t gr_mem_cap;
|
||||
|
||||
/* The getline scan buffer shared by every lookup and stream walk. */
|
||||
static char *gr_line;
|
||||
static size_t gr_cap;
|
||||
|
||||
/* The persistent getgrent stream; NULL when closed. */
|
||||
static FILE *gr_stream;
|
||||
|
||||
/*
|
||||
* Parse the leading digit string of s as an unsigned id. Accepts only
|
||||
* plain decimal digits and rejects empty fields and overflow; returns 0
|
||||
* on success.
|
||||
*/
|
||||
static int
|
||||
gr_parse_id(const char *s, unsigned *out)
|
||||
{
|
||||
unsigned long v = 0;
|
||||
const char *p;
|
||||
|
||||
if (*s == '\0')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
for (p = s; *p != '\0'; p++)
|
||||
{
|
||||
if (*p < '0' || *p > '9')
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
v = v * 10 + (unsigned long)(*p - '0');
|
||||
if (v > 0xffffffffUL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*out = (unsigned)v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Grow the static member pointer array to hold cnt entries plus the
|
||||
* terminating NULL; return 0 on success, -1 when realloc fails. */
|
||||
static int
|
||||
gr_reserve_mem(size_t cnt)
|
||||
{
|
||||
char **newmem;
|
||||
size_t ncap;
|
||||
|
||||
if (gr_mem_cap >= cnt + 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ncap = gr_mem_cap == 0 ? 8 : gr_mem_cap * 2;
|
||||
while (ncap < cnt + 1)
|
||||
{
|
||||
ncap *= 2;
|
||||
}
|
||||
newmem = (char **)realloc((void *)gr_mem, ncap * sizeof(*newmem));
|
||||
if (newmem == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gr_mem = newmem;
|
||||
gr_mem_cap = ncap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the comma-separated member tail in place: every name becomes a
|
||||
* NUL-terminated substring of line, member[i] points at the i-th one and
|
||||
* member[cnt] is NULL. An empty tail yields a single NULL entry. The
|
||||
* parsed group's gr_mem is set to the shared member array.
|
||||
*/
|
||||
static int
|
||||
gr_split_members(char *tail, struct group *gr)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
char *p;
|
||||
|
||||
if (*tail != '\0')
|
||||
{
|
||||
cnt = 1;
|
||||
for (p = tail; *p != '\0'; p++)
|
||||
{
|
||||
if (*p == ',')
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gr_reserve_mem(cnt) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
p = tail;
|
||||
for (size_t i = 0; i < cnt; i++)
|
||||
{
|
||||
char *comma = strchr(p, ',');
|
||||
|
||||
if (comma != NULL)
|
||||
{
|
||||
*comma = '\0';
|
||||
}
|
||||
gr_mem[i] = p;
|
||||
p = comma == NULL ? p + strlen(p) : comma + 1;
|
||||
}
|
||||
gr_mem[cnt] = NULL;
|
||||
gr->gr_mem = gr_mem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split the line (newline already stripped) into its fields in place,
|
||||
* replacing the first three ':' separators with NULs. On success the
|
||||
* struct fields point into line and the member list has been split in
|
||||
* place, and 0 is returned; a record with fewer than three separators or
|
||||
* a non-numeric gid yields -1.
|
||||
*/
|
||||
static int
|
||||
gr_split(struct group *gr, char *line)
|
||||
{
|
||||
char *fields[4];
|
||||
char *p;
|
||||
unsigned gid;
|
||||
int i;
|
||||
|
||||
p = line;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
fields[i] = p;
|
||||
p = strchr(p, ':');
|
||||
if (p == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
*p = '\0';
|
||||
p++;
|
||||
}
|
||||
fields[3] = p;
|
||||
if (gr_parse_id(fields[2], &gid) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (gr_split_members(fields[3], gr) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
gr->gr_name = fields[0];
|
||||
gr->gr_passwd = fields[1];
|
||||
gr->gr_gid = (gid_t)gid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Strip a trailing newline (and CR) from the line just read by getline. */
|
||||
static void
|
||||
gr_chomp(char *line, ssize_t len)
|
||||
{
|
||||
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
|
||||
{
|
||||
line[--len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make line — a chomped, already-split record in the scan buffer — the
|
||||
* non-reentrant result: copy it into the private result buffer and
|
||||
* re-split it there, so the returned struct's strings and member
|
||||
* pointers reference storage the scanner never touches again. Returns
|
||||
* the result struct, or NULL if the result copy could not be grown.
|
||||
*/
|
||||
static struct group *
|
||||
gr_publish(char *line)
|
||||
{
|
||||
size_t len = strlen(line) + 1;
|
||||
|
||||
if (len > gr_out_cap)
|
||||
{
|
||||
char *nb = (char *)realloc(gr_out, len);
|
||||
|
||||
if (nb == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_out = nb;
|
||||
gr_out_cap = len;
|
||||
}
|
||||
strcpy(gr_out, line);
|
||||
if (gr_split(&gr_result, gr_out) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &gr_result;
|
||||
}
|
||||
|
||||
/* Match predicate over a parsed record, keyed by name or gid. */
|
||||
typedef int (*gr_matcher)(const struct group *gr, const void *key);
|
||||
|
||||
static int
|
||||
gr_match_name(const struct group *gr, const void *key)
|
||||
{
|
||||
return strcmp(gr->gr_name, (const char *)key) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gr_match_gid(const struct group *gr, const void *key)
|
||||
{
|
||||
return gr->gr_gid == *(const gid_t *)key;
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-reentrant lookup core: open the database, scan with getline until
|
||||
* the predicate matches, and hand back a pointer into the shared static
|
||||
* line buffer. NULL means "no such entry" (or an unopenable database).
|
||||
*/
|
||||
static struct group *
|
||||
gr_find(gr_matcher match, const void *key)
|
||||
{
|
||||
FILE *f = fopen(GRP_PATH, "r");
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (f == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0 && match(&cur, key))
|
||||
{
|
||||
(void)fclose(f);
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
(void)fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant serializer: copy the parsed record into the caller's struct
|
||||
* and buffer. The string bytes go first; the NULL-terminated member
|
||||
* pointer array follows on a pointer-aligned boundary. Returns 0 on
|
||||
* success, or ERANGE (as the POSIX _r functions return, never touching
|
||||
* errno) when the record needs more than bufsize bytes.
|
||||
*/
|
||||
static int
|
||||
gr_store(const struct group *in, struct group *gr, char *buf, size_t bufsize)
|
||||
{
|
||||
size_t nmem = 0;
|
||||
size_t strneed;
|
||||
size_t ptoff;
|
||||
char *s;
|
||||
char **mem;
|
||||
size_t i;
|
||||
|
||||
while (in->gr_mem != NULL && in->gr_mem[nmem] != NULL)
|
||||
{
|
||||
nmem++;
|
||||
}
|
||||
strneed = strlen(in->gr_name) + 1;
|
||||
strneed += strlen(in->gr_passwd) + 1;
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
strneed += strlen(in->gr_mem[i]) + 1;
|
||||
}
|
||||
ptoff = (strneed + sizeof(char *) - 1) & ~(sizeof(char *) - 1);
|
||||
if (bufsize < ptoff + (nmem + 1) * sizeof(char *))
|
||||
{
|
||||
return ERANGE;
|
||||
}
|
||||
gr->gr_gid = in->gr_gid;
|
||||
mem = (char **)(void *)(buf + ptoff);
|
||||
gr->gr_mem = mem;
|
||||
s = buf;
|
||||
gr->gr_name = s;
|
||||
s += strlen(in->gr_name) + 1;
|
||||
gr->gr_passwd = s;
|
||||
s += strlen(in->gr_passwd) + 1;
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
size_t len = strlen(in->gr_mem[i]) + 1;
|
||||
|
||||
gr->gr_mem[i] = s;
|
||||
s += len;
|
||||
}
|
||||
gr->gr_mem[nmem] = NULL;
|
||||
strcpy(gr->gr_name, in->gr_name);
|
||||
strcpy(gr->gr_passwd, in->gr_passwd);
|
||||
for (i = 0; i < nmem; i++)
|
||||
{
|
||||
size_t len = strlen(in->gr_mem[i]) + 1;
|
||||
|
||||
__builtin_memcpy(gr->gr_mem[i], in->gr_mem[i], len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrnam(const char *name)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return gr_find(gr_match_name, name);
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrgid(gid_t gid)
|
||||
{
|
||||
return gr_find(gr_match_gid, &gid);
|
||||
}
|
||||
|
||||
struct group *
|
||||
getgrent(void)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
gr_stream = fopen(GRP_PATH, "r");
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, gr_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0)
|
||||
{
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setgrent(void)
|
||||
{
|
||||
if (gr_stream != NULL)
|
||||
{
|
||||
rewind(gr_stream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
endgrent(void)
|
||||
{
|
||||
if (gr_stream != NULL)
|
||||
{
|
||||
(void)fclose(gr_stream);
|
||||
gr_stream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reentrant lookup core: scan for the first matching record and serialize
|
||||
* it into the caller's storage. Returns 0 with *result pointing at the
|
||||
* filled struct on success, 0 with *result NULL when no record matches,
|
||||
* ERANGE when the record does not fit bufsize, and EINVAL for a NULL
|
||||
* result pointer.
|
||||
*/
|
||||
static int
|
||||
gr_find_r(gr_matcher match, const void *key, struct group *gr, char *buf, size_t bufsize,
|
||||
struct group **result)
|
||||
{
|
||||
FILE *f;
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || gr == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
f = fopen(GRP_PATH, "r");
|
||||
if (f == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, f);
|
||||
if (got < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) != 0 || !match(&cur, key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = gr_store(&cur, gr, buf, bufsize);
|
||||
(void)fclose(f);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = gr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
(void)fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
getgrnam_r(const char *name, struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
if (name == NULL)
|
||||
{
|
||||
if (result != NULL)
|
||||
{
|
||||
*result = NULL;
|
||||
}
|
||||
return EINVAL;
|
||||
}
|
||||
return gr_find_r(gr_match_name, name, gr, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getgrgid_r(gid_t gid, struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
return gr_find_r(gr_match_gid, &gid, gr, buf, bufsize, result);
|
||||
}
|
||||
|
||||
int
|
||||
getgrent_r(struct group *gr, char *buf, size_t bufsize, struct group **result)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
int rc;
|
||||
|
||||
if (result == NULL || gr == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
if (buf == NULL && bufsize != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
*result = NULL;
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
gr_stream = fopen(GRP_PATH, "r");
|
||||
if (gr_stream == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, gr_stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
rc = gr_store(&cur, gr, buf, bufsize);
|
||||
if (rc == 0)
|
||||
{
|
||||
*result = gr;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
struct group *
|
||||
fgetgrent(FILE *stream)
|
||||
{
|
||||
struct group cur;
|
||||
ssize_t got;
|
||||
|
||||
if (stream == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
got = getline(&gr_line, &gr_cap, stream);
|
||||
if (got < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
gr_chomp(gr_line, got);
|
||||
if (gr_split(&cur, gr_line) == 0)
|
||||
{
|
||||
return gr_publish(gr_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "syscall.h"
|
||||
|
||||
/*
|
||||
* The failing-assertion sink behind <assert.h>.
|
||||
*
|
||||
* Writes the standard-shaped diagnostic to fd 2 through the raw syscall
|
||||
* layer, then traps. No abort(), exit() or stdio: those land in later todos
|
||||
* and an assertion failure must be able to fire from a half-initialized
|
||||
* runtime. __builtin_trap() terminates with SIGILL, which the assert QA
|
||||
* harness expects (the standard only requires abnormal termination).
|
||||
*
|
||||
* Not declared hidden, unlike the other src/internal helpers: <assert.h>
|
||||
* declares this symbol to consumers, so it is part of the public ABI surface
|
||||
* and must resolve across the shared-library boundary.
|
||||
*/
|
||||
|
||||
/* Convert v to decimal digits at p; returns the next free position. */
|
||||
static char *
|
||||
append_dec(char *p, int v)
|
||||
{
|
||||
char tmp[12];
|
||||
int i = 0;
|
||||
|
||||
if (v == 0)
|
||||
{
|
||||
*p++ = '0';
|
||||
return p;
|
||||
}
|
||||
while (v > 0)
|
||||
{
|
||||
tmp[i++] = (char)('0' + v % 10);
|
||||
v /= 10;
|
||||
}
|
||||
while (i > 0)
|
||||
{
|
||||
*p++ = tmp[--i];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Append the string s at p; returns the next free position. */
|
||||
static char *
|
||||
append_str(char *p, const char *s)
|
||||
{
|
||||
while (*s != '\0')
|
||||
{
|
||||
*p++ = *s++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* The __vlibc_assert_fail name sits in the implementation-reserved namespace
|
||||
* by design (it is the libc's private assert plumbing), so the
|
||||
* reserved-identifier check is waived.
|
||||
*/
|
||||
__attribute__((noreturn)) void
|
||||
__vlibc_assert_fail(const char *expr, const char *file, int line,
|
||||
const char *func) // NOLINT(bugprone-reserved-identifier)
|
||||
{
|
||||
char msg[384];
|
||||
char *p = msg;
|
||||
|
||||
p = append_str(p, "Assertion failed: ");
|
||||
p = append_str(p, expr);
|
||||
p = append_str(p, " (file ");
|
||||
p = append_str(p, file);
|
||||
p = append_str(p, ": line ");
|
||||
p = append_dec(p, line);
|
||||
p = append_str(p, ", func ");
|
||||
p = append_str(p, func);
|
||||
p = append_str(p, ")\n");
|
||||
|
||||
(void)__syscall3(SYS_write, 2, (long)msg, (long)(p - msg));
|
||||
__builtin_trap();
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
#ifndef VLIBC_INTERNAL_ATOMIC_H
|
||||
#define VLIBC_INTERNAL_ATOMIC_H
|
||||
|
||||
/*
|
||||
* vlibc — internal atomics.
|
||||
*
|
||||
* Thin wrappers over the GCC __atomic_* builtins, so internal code writes
|
||||
* portable-looking atomics without depending on a particular <stdatomic.h>.
|
||||
* The wrappers operate on int and long (the widths internal counters and
|
||||
* refcounts actually use); pointers get their own load/store/cmpxchg trio.
|
||||
*
|
||||
* All wrappers are sequentially consistent by default: a libc-internal
|
||||
* operation is almost never hot enough to justify weakening, and a single
|
||||
* memory order everywhere is the least error-prone choice.
|
||||
*
|
||||
* The fetch_or / fetch_and / fetch_sub family and the add_fetch / sub_fetch
|
||||
* result-returning forms were added by todo 44 for the futex lock words of
|
||||
* the pthread layer (#46-#48): futex wait words are built with or/and
|
||||
* (musl's a_or/a_and) and waiter counts with add/sub that return the NEW
|
||||
* value (musl's a_inc/a_dec pair). The fetch_* forms return the OLD value,
|
||||
* matching the C11/GCC __atomic_fetch_* convention; the *_fetch forms
|
||||
* return the NEW one.
|
||||
*/
|
||||
|
||||
static inline int
|
||||
atomic_load(const volatile int *p)
|
||||
{
|
||||
return __atomic_load_n(p, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline void
|
||||
atomic_store(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
__atomic_store_n(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_add(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_add_fetch(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_sub(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_sub_fetch(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_sub_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_or(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_fetch_and(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline int
|
||||
atomic_exchange(volatile int *p, int v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_exchange_n(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare-and-swap: store want into *p iff *p == expect; return the value
|
||||
* previously in *p (equal to expect on success). The (expect, want) order
|
||||
* matches the C11 atomic_compare_exchange and GCC builtin convention.
|
||||
*/
|
||||
static inline int
|
||||
atomic_cas(volatile int *p, int expect,
|
||||
int want) // NOLINT(bugprone-easily-swappable-parameters,readability-non-const-parameter)
|
||||
{
|
||||
int old = expect;
|
||||
|
||||
__atomic_compare_exchange_n(p, &old, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_load_l(const volatile long *p)
|
||||
{
|
||||
return __atomic_load_n(p, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline void
|
||||
atomic_store_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
__atomic_store_n(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_add_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_add(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_add_fetch_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_add_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_sub_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_sub_fetch_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_sub_fetch(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_or_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_or(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_fetch_and_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_fetch_and(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_exchange_l(volatile long *p, long v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
return __atomic_exchange_n(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline long
|
||||
atomic_cas_l(
|
||||
volatile long *p, long expect,
|
||||
long want) // NOLINT(bugprone-easily-swappable-parameters,readability-non-const-parameter)
|
||||
{
|
||||
long old = expect;
|
||||
|
||||
__atomic_compare_exchange_n(p, &old, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
atomic_load_p(void *const volatile *p)
|
||||
{
|
||||
return __atomic_load_n(p, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline void
|
||||
atomic_store_p(void *volatile *p, void *v) // NOLINT(readability-non-const-parameter)
|
||||
{
|
||||
__atomic_store_n(p, v, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
atomic_cas_p(
|
||||
void *volatile *p, void *expect,
|
||||
void *want) // NOLINT(bugprone-easily-swappable-parameters,readability-non-const-parameter)
|
||||
{
|
||||
void *old = expect;
|
||||
|
||||
__atomic_compare_exchange_n(p, &old, want, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
||||
return old;
|
||||
}
|
||||
|
||||
static inline void
|
||||
atomic_fence(void)
|
||||
{
|
||||
__atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif /* VLIBC_INTERNAL_ATOMIC_H */
|
||||
@@ -0,0 +1,16 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "errno.h"
|
||||
|
||||
/*
|
||||
* Return the address of the calling thread's errno slot: the TCB address
|
||||
* (read through the x86_64 FS thread pointer) plus the fixed errno-slot
|
||||
* offset, an ABI constant defined in errno.h and consumed by the TCB layout.
|
||||
*/
|
||||
int *
|
||||
__errno_location(void) // NOLINT(bugprone-reserved-identifier)
|
||||
{
|
||||
return (int *)((char *)__builtin_thread_pointer() + VLIBC_TCB_ERRNO_OFF);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef VLIBC_INTERNAL_ERRNO_H
|
||||
#define VLIBC_INTERNAL_ERRNO_H
|
||||
|
||||
/*
|
||||
* vlibc — internal errno (thread-local via the TCB).
|
||||
*
|
||||
* errno is per-thread state. It lives in a slot of the thread control block
|
||||
* (TCB), addressed relative to the x86_64 FS thread pointer; there is no
|
||||
* process-global errno object and no compiler-managed TLS (`__thread`) here.
|
||||
*
|
||||
* The public <errno.h> is the single canonical home for the errno macro, the
|
||||
* __errno_location() declaration, and the E* constant table (1..133, with
|
||||
* EAGAIN/EWOULDBLOCK and EDEADLK/EDEADLOCK aliases and no values 41/58).
|
||||
* This header includes it instead of keeping a second copy, so the public
|
||||
* and internal views of errno can never drift apart. What stays internal-
|
||||
* only here is the errno TCB-slot offset, an ABI constant the public header
|
||||
* must not expose.
|
||||
*
|
||||
* The offset of the errno slot inside the TCB is an ABI constant defined
|
||||
* below and consumed by the authoritative TCB layout (see the startup todo,
|
||||
* which owns the TCB/DTV layout). No other layer may re-derive errno's
|
||||
* location: it must reference VLIBC_TCB_ERRNO_OFF.
|
||||
*
|
||||
* Slot 0 (offset 0) of the TCB is reserved for the TCB self pointer by
|
||||
* convention: __errno_location() reads the thread pointer with
|
||||
* __builtin_thread_pointer(), which on x86_64 loads the value at %fs:0, i.e.
|
||||
* the self pointer the TCB layout must store at its first word. The errno
|
||||
* slot therefore starts at offset 8, immediately after the self pointer, so
|
||||
* it shares the TCB's first cache line (it is the hottest TCB member). The
|
||||
* slot is 8 bytes for natural alignment; errno occupies the first 4.
|
||||
*/
|
||||
#define VLIBC_TCB_ERRNO_OFF 8
|
||||
|
||||
/* errno, __errno_location(), and the E* table live in the public header. */
|
||||
#include <errno.h>
|
||||
|
||||
#endif /* VLIBC_INTERNAL_ERRNO_H */
|
||||
@@ -0,0 +1,101 @@
|
||||
#ifndef VLIBC_INTERNAL_FUTEX_H
|
||||
#define VLIBC_INTERNAL_FUTEX_H
|
||||
|
||||
/*
|
||||
* vlibc — internal futex substrate (todo 44).
|
||||
*
|
||||
* Linux futex operations over SYS_futex, the kernel's process-shared sleep
|
||||
* primitive that every lock, condvar, barrier, and rwlock in the library
|
||||
* rests on. Two calling conventions are deliberately provided:
|
||||
*
|
||||
* - __futex() is the RAW seam: it returns exactly what the kernel put in
|
||||
* rax (0 or a positive value on success, a NEGATIVE errno on failure)
|
||||
* and never touches errno. Consumers that need custom error handling
|
||||
* (retry-on-EINTR, -ENOSYS fallbacks, or errno-code returns) use this.
|
||||
*
|
||||
* - __futex_wait() / __futex_timedwait() / __futex_wake() are the mapped
|
||||
* wrappers in the house syscall_ret() convention: 0 (or the number of
|
||||
* waiters woken) on success, -1 with errno set on failure. These are
|
||||
* what the pthread layer (#45-#48) calls.
|
||||
*
|
||||
* Op codes and flags are kernel UAPI constants (linux/futex.h); the values
|
||||
* are pinned below so this header stays self-contained. FUTEX_PRIVATE adds
|
||||
* no extra atomics: the wait word is shared only between threads of one
|
||||
* process. The ancient-kernel ENOSYS fallback path musl carries (retry
|
||||
* without FUTEX_PRIVATE) is deliberately NOT implemented — every kernel
|
||||
* since 2.6.22 handles the private flag, and the plan targets modern
|
||||
* x86_64 only.
|
||||
*
|
||||
* Timeout semantics: __futex_timedwait() takes an ABSOLUTE deadline and
|
||||
* uses FUTEX_WAIT_BITSET (kernel-compared, no userspace clock read):
|
||||
* CLOCK_MONOTONIC by default, CLOCK_REALTIME when the FUTEX_CLOCK_REALTIME
|
||||
* flag is added. A deadline already in the past returns -1/ETIMEDOUT from
|
||||
* the kernel immediately. musl's relative-timeout __timedwait shape is not
|
||||
* copied: it exists there only to keep pre-WAIT_BITSET kernels working.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "../internal/libc.h"
|
||||
|
||||
/* futex op codes (linux/futex.h). */
|
||||
#define FUTEX_WAIT 0
|
||||
#define FUTEX_WAKE 1
|
||||
#define FUTEX_FD 2
|
||||
#define FUTEX_REQUEUE 3
|
||||
#define FUTEX_CMP_REQUEUE 4
|
||||
#define FUTEX_WAKE_OP 5
|
||||
#define FUTEX_LOCK_PI 6
|
||||
#define FUTEX_UNLOCK_PI 7
|
||||
#define FUTEX_TRYLOCK_PI 8
|
||||
#define FUTEX_WAIT_BITSET 9
|
||||
|
||||
/* op flags (linux/futex.h). */
|
||||
#define FUTEX_PRIVATE 128
|
||||
#define FUTEX_CLOCK_REALTIME 256
|
||||
|
||||
/* bitset for "any bits match" (linux/futex.h). */
|
||||
#define FUTEX_BITSET_MATCH_ANY 0xffffffffu
|
||||
|
||||
/*
|
||||
* The __futex family lives in the implementation-reserved namespace, same
|
||||
* as the __syscall<n> seam — intentional, waived here for the whole block.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
|
||||
/*
|
||||
* Raw futex syscall: return the kernel's raw result — 0 or a positive value
|
||||
* on success, a negative errno on failure. errno is never touched. op may
|
||||
* be any FUTEX_* op code; ts is the timeout pointer for FUTEX_WAIT /
|
||||
* FUTEX_WAIT_BITSET, NULL for the others.
|
||||
*/
|
||||
hidden long
|
||||
__futex(volatile int *addr, int op, int val, void *ts);
|
||||
|
||||
/*
|
||||
* Mapped wait: block until *addr != val or a wakeup arrives. Returns 0 on
|
||||
* wakeup, -1 with errno set on failure. priv != 0 selects FUTEX_PRIVATE.
|
||||
*/
|
||||
hidden int
|
||||
__futex_wait(volatile int *addr, int val, int priv);
|
||||
|
||||
/*
|
||||
* Mapped timed wait: like __futex_wait, but with an ABSOLUTE deadline on
|
||||
* clock clk (CLOCK_MONOTONIC or CLOCK_REALTIME). Returns 0 on wakeup, -1
|
||||
* with errno set (ETIMEDOUT when the deadline passes, EINTR when a signal
|
||||
* interrupts the wait) on failure.
|
||||
*/
|
||||
hidden int
|
||||
__futex_timedwait(volatile int *addr, int val, int priv, int clk, const struct timespec *at);
|
||||
|
||||
/*
|
||||
* Mapped wake: wake up to cnt waiters on *addr (cnt < 0 = wake all, the
|
||||
* INT_MAX kernel convention musl uses). Returns the number of waiters
|
||||
* actually woken, or -1 with errno set on failure.
|
||||
*/
|
||||
hidden int
|
||||
__futex_wake(volatile int *addr, int cnt, int priv);
|
||||
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_FUTEX_H */
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef VLIBC_INTERNAL_LIBC_H
|
||||
#define VLIBC_INTERNAL_LIBC_H
|
||||
|
||||
/*
|
||||
* vlibc — internal compiler-attribute shorthand.
|
||||
*
|
||||
* Everything internal to the library is declared with hidden visibility so
|
||||
* that neither the static archive nor the shared library exports symbols
|
||||
* outside the public API surface. The shared-library case matters most:
|
||||
* without `hidden`, every internal helper would become a dynamic symbol that
|
||||
* could be interposed or collide with a consumer's own symbols.
|
||||
*/
|
||||
#define hidden __attribute__((__visibility__("hidden")))
|
||||
|
||||
#endif /* VLIBC_INTERNAL_LIBC_H */
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef VLIBC_INTERNAL_MALLOC_H
|
||||
#define VLIBC_INTERNAL_MALLOC_H
|
||||
|
||||
/*
|
||||
* vlibc — internal allocator seam (todo 8).
|
||||
*
|
||||
* The allocator todo (#7) owns the real implementation and provides these
|
||||
* entry points; library code that must allocate (strdup, strndup, and later
|
||||
* stdio/regex/...) calls them instead of the public malloc/free so that the
|
||||
* allocator remains the single allocation implementation. The public
|
||||
* malloc/free/calloc/realloc of todo 7 must be backed by the same allocator,
|
||||
* so a block returned by __libc_malloc can be released with public free.
|
||||
*
|
||||
* - __libc_malloc(n): allocate n bytes, 16-byte aligned; NULL + errno ENOMEM
|
||||
* on failure. May return a unique pointer even when n == 0.
|
||||
* - __libc_free(p): release a block returned by __libc_malloc; NULL is a
|
||||
* no-op.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "libc.h"
|
||||
|
||||
hidden void *
|
||||
__libc_malloc(size_t n); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
hidden void
|
||||
__libc_free(void *p); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
#endif /* VLIBC_INTERNAL_MALLOC_H */
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef VLIBC_INTERNAL_STRTOX_H
|
||||
#define VLIBC_INTERNAL_STRTOX_H
|
||||
|
||||
/*
|
||||
* vlibc — internal numeric-scan engine (todo 11).
|
||||
*
|
||||
* This header declares the shared core of the strtol/strtoul family. The
|
||||
* engine performs C23 7.24.1.4 steps 1-7 — whitespace skip, optional sign,
|
||||
* base resolution (0/2..36 with the 0x/0 prefix rules), and saturating
|
||||
* digit accumulation — and reports the subject sequence's magnitude, sign,
|
||||
* and overflow status. The public wrappers in src/stdlib/strtox_impl.c own
|
||||
* the per-type clamping and errno policy (signed clamps at LONG_MIN/LONG_MAX
|
||||
* with ERANGE; unsigned negates in the return type modulo 2^width, with
|
||||
* ERANGE only when the magnitude itself overflows the type, C23 7.24.1.4p8).
|
||||
*
|
||||
* Todo 14 (strtoimax/strtoumax, which on x86_64 are long/unsigned long)
|
||||
* consumes this header and reuses the engine unchanged; the wcsto* family
|
||||
* only needs a byte-level shim before it.
|
||||
*
|
||||
* The helpers below are static inline so the engine and every wrapper share
|
||||
* one C-locale whitespace/digit definition without a call boundary.
|
||||
*/
|
||||
|
||||
#include "libc.h"
|
||||
|
||||
/* Scan results. */
|
||||
#define STRTOX_OK 0 /* subject sequence present (digits > 0) */
|
||||
#define STRTOX_EMPTY 1 /* no subject sequence; *endptr = nptr */
|
||||
#define STRTOX_BADBASE 2 /* base not in {0} union [2, 36]; *endptr = nptr */
|
||||
|
||||
/*
|
||||
* Scan the subject sequence at nptr (C23 7.24.1.4 steps 1-7).
|
||||
*
|
||||
* On STRTOX_OK: *mag holds the magnitude (exact unless *over), *neg the
|
||||
* sign, *endptr the position past the last digit, *over whether the
|
||||
* magnitude exceeded unsigned long long (callers must clamp + ERANGE).
|
||||
* On STRTOX_EMPTY / STRTOX_BADBASE: *endptr is set to nptr, *mag and *neg
|
||||
* are zeroed; the caller sets errno = EINVAL for STRTOX_BADBASE and
|
||||
* returns 0.
|
||||
*/
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier)
|
||||
hidden int
|
||||
__strtox_scan(const char *nptr, char **endptr, int base, int *neg, unsigned long long *mag,
|
||||
int *over);
|
||||
// NOLINTEND(bugprone-reserved-identifier)
|
||||
|
||||
/* C-locale whitespace: exactly ' ' and '\t'..'\r'. */
|
||||
static inline int
|
||||
strtox_isspace(int c)
|
||||
{
|
||||
return c == ' ' || (unsigned int)c - '\t' < 5U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Value of c as a base-36 digit, or -1 when c is not an ASCII letter/digit.
|
||||
* c | 0x20 folds 'A'..'F' to 'a'..'f'; the unsigned cast keeps negative
|
||||
* values out of the range checks (same arithmetic-range idiom as ctype.c).
|
||||
*/
|
||||
static inline int
|
||||
strtox_digit(int c)
|
||||
{
|
||||
if ((unsigned int)c - '0' < 10U)
|
||||
{
|
||||
return c - '0';
|
||||
}
|
||||
if ((unsigned int)(c | 0x20) - 'a' < 26U)
|
||||
{
|
||||
return (c | 0x20) - 'a' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_INTERNAL_STRTOX_H */
|
||||
@@ -0,0 +1,423 @@
|
||||
#ifndef VLIBC_INTERNAL_SYSCALL_H
|
||||
#define VLIBC_INTERNAL_SYSCALL_H
|
||||
|
||||
/*
|
||||
* vlibc — internal raw syscall layer.
|
||||
*
|
||||
* This header is the single kernel-ABI seam of the library: every syscall
|
||||
* vlibc makes goes through the __syscall<n>() entry points below (defined
|
||||
* per-architecture in arch/x86_64/syscall_arch.h) and its result is
|
||||
* translated by syscall_ret() into the C return-code + errno convention.
|
||||
*
|
||||
* The SYS_* table below holds the x86_64 Linux syscall numbers, transcribed
|
||||
* from the kernel's arch/x86/entry/syscalls/syscall_64.tbl (the same values
|
||||
* glibc exposes as <asm/unistd_64.h>). These are pure kernel-ABI facts: the
|
||||
* x86_64 table, NOT the asm-generic table, which differs on several numbers
|
||||
* (e.g. x86_64 SYS_open is 2, SYS_mmap is 9). Numbers 337-423 are reserved
|
||||
* by the kernel and intentionally absent.
|
||||
*/
|
||||
|
||||
#include "../../arch/x86_64/syscall_arch.h" // IWYU pragma: keep
|
||||
|
||||
#include "../internal/libc.h"
|
||||
|
||||
#define SYS_read 0
|
||||
#define SYS_write 1
|
||||
#define SYS_open 2
|
||||
#define SYS_close 3
|
||||
#define SYS_stat 4
|
||||
#define SYS_fstat 5
|
||||
#define SYS_lstat 6
|
||||
#define SYS_poll 7
|
||||
#define SYS_lseek 8
|
||||
#define SYS_mmap 9
|
||||
#define SYS_mprotect 10
|
||||
#define SYS_munmap 11
|
||||
#define SYS_brk 12
|
||||
#define SYS_rt_sigaction 13
|
||||
#define SYS_rt_sigprocmask 14
|
||||
#define SYS_rt_sigreturn 15
|
||||
#define SYS_ioctl 16
|
||||
#define SYS_pread64 17
|
||||
#define SYS_pwrite64 18
|
||||
#define SYS_readv 19
|
||||
#define SYS_writev 20
|
||||
#define SYS_access 21
|
||||
#define SYS_pipe 22
|
||||
#define SYS_select 23
|
||||
#define SYS_sched_yield 24
|
||||
#define SYS_mremap 25
|
||||
#define SYS_msync 26
|
||||
#define SYS_mincore 27
|
||||
#define SYS_madvise 28
|
||||
#define SYS_shmget 29
|
||||
#define SYS_shmat 30
|
||||
#define SYS_shmctl 31
|
||||
#define SYS_dup 32
|
||||
#define SYS_dup2 33
|
||||
#define SYS_pause 34
|
||||
#define SYS_nanosleep 35
|
||||
#define SYS_getitimer 36
|
||||
#define SYS_alarm 37
|
||||
#define SYS_setitimer 38
|
||||
#define SYS_getpid 39
|
||||
#define SYS_sendfile 40
|
||||
#define SYS_socket 41
|
||||
#define SYS_connect 42
|
||||
#define SYS_accept 43
|
||||
#define SYS_sendto 44
|
||||
#define SYS_recvfrom 45
|
||||
#define SYS_sendmsg 46
|
||||
#define SYS_recvmsg 47
|
||||
#define SYS_shutdown 48
|
||||
#define SYS_bind 49
|
||||
#define SYS_listen 50
|
||||
#define SYS_getsockname 51
|
||||
#define SYS_getpeername 52
|
||||
#define SYS_socketpair 53
|
||||
#define SYS_setsockopt 54
|
||||
#define SYS_getsockopt 55
|
||||
#define SYS_clone 56
|
||||
#define SYS_fork 57
|
||||
#define SYS_vfork 58
|
||||
#define SYS_execve 59
|
||||
#define SYS_exit 60
|
||||
#define SYS_wait4 61
|
||||
#define SYS_kill 62
|
||||
#define SYS_uname 63
|
||||
#define SYS_semget 64
|
||||
#define SYS_semop 65
|
||||
#define SYS_semctl 66
|
||||
#define SYS_shmdt 67
|
||||
#define SYS_msgget 68
|
||||
#define SYS_msgsnd 69
|
||||
#define SYS_msgrcv 70
|
||||
#define SYS_msgctl 71
|
||||
#define SYS_fcntl 72
|
||||
#define SYS_flock 73
|
||||
#define SYS_fsync 74
|
||||
#define SYS_fdatasync 75
|
||||
#define SYS_truncate 76
|
||||
#define SYS_ftruncate 77
|
||||
#define SYS_getdents 78
|
||||
#define SYS_getcwd 79
|
||||
#define SYS_chdir 80
|
||||
#define SYS_fchdir 81
|
||||
#define SYS_rename 82
|
||||
#define SYS_mkdir 83
|
||||
#define SYS_rmdir 84
|
||||
#define SYS_creat 85
|
||||
#define SYS_link 86
|
||||
#define SYS_unlink 87
|
||||
#define SYS_symlink 88
|
||||
#define SYS_readlink 89
|
||||
#define SYS_chmod 90
|
||||
#define SYS_fchmod 91
|
||||
#define SYS_chown 92
|
||||
#define SYS_fchown 93
|
||||
#define SYS_lchown 94
|
||||
#define SYS_umask 95
|
||||
#define SYS_gettimeofday 96
|
||||
#define SYS_getrlimit 97
|
||||
#define SYS_getrusage 98
|
||||
#define SYS_sysinfo 99
|
||||
#define SYS_times 100
|
||||
#define SYS_ptrace 101
|
||||
#define SYS_getuid 102
|
||||
#define SYS_syslog 103
|
||||
#define SYS_getgid 104
|
||||
#define SYS_setuid 105
|
||||
#define SYS_setgid 106
|
||||
#define SYS_geteuid 107
|
||||
#define SYS_getegid 108
|
||||
#define SYS_setpgid 109
|
||||
#define SYS_getppid 110
|
||||
#define SYS_getpgrp 111
|
||||
#define SYS_setsid 112
|
||||
#define SYS_setreuid 113
|
||||
#define SYS_setregid 114
|
||||
#define SYS_getgroups 115
|
||||
#define SYS_setgroups 116
|
||||
#define SYS_setresuid 117
|
||||
#define SYS_getresuid 118
|
||||
#define SYS_setresgid 119
|
||||
#define SYS_getresgid 120
|
||||
#define SYS_getpgid 121
|
||||
#define SYS_setfsuid 122
|
||||
#define SYS_setfsgid 123
|
||||
#define SYS_getsid 124
|
||||
#define SYS_capget 125
|
||||
#define SYS_capset 126
|
||||
#define SYS_rt_sigpending 127
|
||||
#define SYS_rt_sigtimedwait 128
|
||||
#define SYS_rt_sigqueueinfo 129
|
||||
#define SYS_rt_sigsuspend 130
|
||||
#define SYS_sigaltstack 131
|
||||
#define SYS_utime 132
|
||||
#define SYS_mknod 133
|
||||
#define SYS_uselib 134
|
||||
#define SYS_personality 135
|
||||
#define SYS_ustat 136
|
||||
#define SYS_statfs 137
|
||||
#define SYS_fstatfs 138
|
||||
#define SYS_sysfs 139
|
||||
#define SYS_getpriority 140
|
||||
#define SYS_setpriority 141
|
||||
#define SYS_sched_setparam 142
|
||||
#define SYS_sched_getparam 143
|
||||
#define SYS_sched_setscheduler 144
|
||||
#define SYS_sched_getscheduler 145
|
||||
#define SYS_sched_get_priority_max 146
|
||||
#define SYS_sched_get_priority_min 147
|
||||
#define SYS_sched_rr_get_interval 148
|
||||
#define SYS_mlock 149
|
||||
#define SYS_munlock 150
|
||||
#define SYS_mlockall 151
|
||||
#define SYS_munlockall 152
|
||||
#define SYS_vhangup 153
|
||||
#define SYS_modify_ldt 154
|
||||
#define SYS_pivot_root 155
|
||||
#define SYS__sysctl 156
|
||||
#define SYS_prctl 157
|
||||
#define SYS_arch_prctl 158
|
||||
#define SYS_adjtimex 159
|
||||
#define SYS_setrlimit 160
|
||||
#define SYS_chroot 161
|
||||
#define SYS_sync 162
|
||||
#define SYS_acct 163
|
||||
#define SYS_settimeofday 164
|
||||
#define SYS_mount 165
|
||||
#define SYS_umount2 166
|
||||
#define SYS_swapon 167
|
||||
#define SYS_swapoff 168
|
||||
#define SYS_reboot 169
|
||||
#define SYS_sethostname 170
|
||||
#define SYS_setdomainname 171
|
||||
#define SYS_iopl 172
|
||||
#define SYS_ioperm 173
|
||||
#define SYS_create_module 174
|
||||
#define SYS_init_module 175
|
||||
#define SYS_delete_module 176
|
||||
#define SYS_get_kernel_syms 177
|
||||
#define SYS_query_module 178
|
||||
#define SYS_quotactl 179
|
||||
#define SYS_nfsservctl 180
|
||||
#define SYS_getpmsg 181
|
||||
#define SYS_putpmsg 182
|
||||
#define SYS_afs_syscall 183
|
||||
#define SYS_tuxcall 184
|
||||
#define SYS_security 185
|
||||
#define SYS_gettid 186
|
||||
#define SYS_readahead 187
|
||||
#define SYS_setxattr 188
|
||||
#define SYS_lsetxattr 189
|
||||
#define SYS_fsetxattr 190
|
||||
#define SYS_getxattr 191
|
||||
#define SYS_lgetxattr 192
|
||||
#define SYS_fgetxattr 193
|
||||
#define SYS_listxattr 194
|
||||
#define SYS_llistxattr 195
|
||||
#define SYS_flistxattr 196
|
||||
#define SYS_removexattr 197
|
||||
#define SYS_lremovexattr 198
|
||||
#define SYS_fremovexattr 199
|
||||
#define SYS_tkill 200
|
||||
#define SYS_time 201
|
||||
#define SYS_futex 202
|
||||
#define SYS_sched_setaffinity 203
|
||||
#define SYS_sched_getaffinity 204
|
||||
#define SYS_set_thread_area 205
|
||||
#define SYS_io_setup 206
|
||||
#define SYS_io_destroy 207
|
||||
#define SYS_io_getevents 208
|
||||
#define SYS_io_submit 209
|
||||
#define SYS_io_cancel 210
|
||||
#define SYS_get_thread_area 211
|
||||
#define SYS_lookup_dcookie 212
|
||||
#define SYS_epoll_create 213
|
||||
#define SYS_epoll_ctl_old 214
|
||||
#define SYS_epoll_wait_old 215
|
||||
#define SYS_remap_file_pages 216
|
||||
#define SYS_getdents64 217
|
||||
#define SYS_set_tid_address 218
|
||||
#define SYS_restart_syscall 219
|
||||
#define SYS_semtimedop 220
|
||||
#define SYS_fadvise64 221
|
||||
#define SYS_timer_create 222
|
||||
#define SYS_timer_settime 223
|
||||
#define SYS_timer_gettime 224
|
||||
#define SYS_timer_getoverrun 225
|
||||
#define SYS_timer_delete 226
|
||||
#define SYS_clock_settime 227
|
||||
#define SYS_clock_gettime 228
|
||||
#define SYS_clock_getres 229
|
||||
#define SYS_clock_nanosleep 230
|
||||
#define SYS_exit_group 231
|
||||
#define SYS_epoll_wait 232
|
||||
#define SYS_epoll_ctl 233
|
||||
#define SYS_tgkill 234
|
||||
#define SYS_utimes 235
|
||||
#define SYS_vserver 236
|
||||
#define SYS_mbind 237
|
||||
#define SYS_set_mempolicy 238
|
||||
#define SYS_get_mempolicy 239
|
||||
#define SYS_mq_open 240
|
||||
#define SYS_mq_unlink 241
|
||||
#define SYS_mq_timedsend 242
|
||||
#define SYS_mq_timedreceive 243
|
||||
#define SYS_mq_notify 244
|
||||
#define SYS_mq_getsetattr 245
|
||||
#define SYS_kexec_load 246
|
||||
#define SYS_waitid 247
|
||||
#define SYS_add_key 248
|
||||
#define SYS_request_key 249
|
||||
#define SYS_keyctl 250
|
||||
#define SYS_ioprio_set 251
|
||||
#define SYS_ioprio_get 252
|
||||
#define SYS_inotify_init 253
|
||||
#define SYS_inotify_add_watch 254
|
||||
#define SYS_inotify_rm_watch 255
|
||||
#define SYS_migrate_pages 256
|
||||
#define SYS_openat 257
|
||||
#define SYS_mkdirat 258
|
||||
#define SYS_mknodat 259
|
||||
#define SYS_fchownat 260
|
||||
#define SYS_futimesat 261
|
||||
#define SYS_newfstatat 262
|
||||
#define SYS_unlinkat 263
|
||||
#define SYS_renameat 264
|
||||
#define SYS_linkat 265
|
||||
#define SYS_symlinkat 266
|
||||
#define SYS_readlinkat 267
|
||||
#define SYS_fchmodat 268
|
||||
#define SYS_faccessat 269
|
||||
#define SYS_pselect6 270
|
||||
#define SYS_ppoll 271
|
||||
#define SYS_unshare 272
|
||||
#define SYS_set_robust_list 273
|
||||
#define SYS_get_robust_list 274
|
||||
#define SYS_splice 275
|
||||
#define SYS_tee 276
|
||||
#define SYS_sync_file_range 277
|
||||
#define SYS_vmsplice 278
|
||||
#define SYS_move_pages 279
|
||||
#define SYS_utimensat 280
|
||||
#define SYS_epoll_pwait 281
|
||||
#define SYS_signalfd 282
|
||||
#define SYS_timerfd_create 283
|
||||
#define SYS_eventfd 284
|
||||
#define SYS_fallocate 285
|
||||
#define SYS_timerfd_settime 286
|
||||
#define SYS_timerfd_gettime 287
|
||||
#define SYS_accept4 288
|
||||
#define SYS_signalfd4 289
|
||||
#define SYS_eventfd2 290
|
||||
#define SYS_epoll_create1 291
|
||||
#define SYS_dup3 292
|
||||
#define SYS_pipe2 293
|
||||
#define SYS_inotify_init1 294
|
||||
#define SYS_preadv 295
|
||||
#define SYS_pwritev 296
|
||||
#define SYS_rt_tgsigqueueinfo 297
|
||||
#define SYS_perf_event_open 298
|
||||
#define SYS_recvmmsg 299
|
||||
#define SYS_fanotify_init 300
|
||||
#define SYS_fanotify_mark 301
|
||||
#define SYS_prlimit64 302
|
||||
#define SYS_name_to_handle_at 303
|
||||
#define SYS_open_by_handle_at 304
|
||||
#define SYS_clock_adjtime 305
|
||||
#define SYS_syncfs 306
|
||||
#define SYS_sendmmsg 307
|
||||
#define SYS_setns 308
|
||||
#define SYS_getcpu 309
|
||||
#define SYS_process_vm_readv 310
|
||||
#define SYS_process_vm_writev 311
|
||||
#define SYS_kcmp 312
|
||||
#define SYS_finit_module 313
|
||||
#define SYS_sched_setattr 314
|
||||
#define SYS_sched_getattr 315
|
||||
#define SYS_renameat2 316
|
||||
#define SYS_seccomp 317
|
||||
#define SYS_getrandom 318
|
||||
#define SYS_memfd_create 319
|
||||
#define SYS_kexec_file_load 320
|
||||
#define SYS_bpf 321
|
||||
#define SYS_execveat 322
|
||||
#define SYS_userfaultfd 323
|
||||
#define SYS_membarrier 324
|
||||
#define SYS_mlock2 325
|
||||
#define SYS_copy_file_range 326
|
||||
#define SYS_preadv2 327
|
||||
#define SYS_pwritev2 328
|
||||
#define SYS_pkey_mprotect 329
|
||||
#define SYS_pkey_alloc 330
|
||||
#define SYS_pkey_free 331
|
||||
#define SYS_statx 332
|
||||
#define SYS_io_pgetevents 333
|
||||
#define SYS_rseq 334
|
||||
#define SYS_uretprobe 335
|
||||
#define SYS_uprobe 336
|
||||
#define SYS_pidfd_send_signal 424
|
||||
#define SYS_io_uring_setup 425
|
||||
#define SYS_io_uring_enter 426
|
||||
#define SYS_io_uring_register 427
|
||||
#define SYS_open_tree 428
|
||||
#define SYS_move_mount 429
|
||||
#define SYS_fsopen 430
|
||||
#define SYS_fsconfig 431
|
||||
#define SYS_fsmount 432
|
||||
#define SYS_fspick 433
|
||||
#define SYS_pidfd_open 434
|
||||
#define SYS_clone3 435
|
||||
#define SYS_close_range 436
|
||||
#define SYS_openat2 437
|
||||
#define SYS_pidfd_getfd 438
|
||||
#define SYS_faccessat2 439
|
||||
#define SYS_process_madvise 440
|
||||
#define SYS_epoll_pwait2 441
|
||||
#define SYS_mount_setattr 442
|
||||
#define SYS_quotactl_fd 443
|
||||
#define SYS_landlock_create_ruleset 444
|
||||
#define SYS_landlock_add_rule 445
|
||||
#define SYS_landlock_restrict_self 446
|
||||
#define SYS_memfd_secret 447
|
||||
#define SYS_process_mrelease 448
|
||||
#define SYS_futex_waitv 449
|
||||
#define SYS_set_mempolicy_home_node 450
|
||||
#define SYS_cachestat 451
|
||||
#define SYS_fchmodat2 452
|
||||
#define SYS_map_shadow_stack 453
|
||||
#define SYS_futex_wake 454
|
||||
#define SYS_futex_wait 455
|
||||
#define SYS_futex_requeue 456
|
||||
#define SYS_statmount 457
|
||||
#define SYS_listmount 458
|
||||
#define SYS_lsm_get_self_attr 459
|
||||
#define SYS_lsm_set_self_attr 460
|
||||
#define SYS_lsm_list_modules 461
|
||||
#define SYS_mseal 462
|
||||
#define SYS_setxattrat 463
|
||||
#define SYS_getxattrat 464
|
||||
#define SYS_listxattrat 465
|
||||
#define SYS_removexattrat 466
|
||||
#define SYS_open_tree_attr 467
|
||||
#define SYS_file_getattr 468
|
||||
#define SYS_file_setattr 469
|
||||
#define SYS_listns 470
|
||||
#define SYS_rseq_slice_yield 471
|
||||
|
||||
/*
|
||||
* Translate a raw syscall result into the C convention.
|
||||
*
|
||||
* On success (rax >= 0) the value is returned unchanged. On error the kernel
|
||||
* returns -errno in rax; syscall_ret stores errno into the calling thread's
|
||||
* TCB errno slot (via __errno_location()) and returns -1. The -4095 bound
|
||||
* matches the kernel's MAX_ERRNO: results at or below -4096 are legitimate
|
||||
* values (e.g. mmap addresses cannot happen to land here, but the rule is
|
||||
* kept exact).
|
||||
*/
|
||||
hidden int
|
||||
syscall_ret(long r);
|
||||
|
||||
#endif /* VLIBC_INTERNAL_SYSCALL_H */
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "errno.h"
|
||||
#include "syscall.h"
|
||||
|
||||
/*
|
||||
* Translate a raw syscall result into the C convention: return the value on
|
||||
* success, or store the negated errno and return -1 on error. The -4095
|
||||
* bound is the kernel's MAX_ERRNO: any result at or below -4096 is a
|
||||
* legitimate value, not an error code.
|
||||
*/
|
||||
int
|
||||
syscall_ret(long r)
|
||||
{
|
||||
if (r < 0 && r > -4096)
|
||||
{
|
||||
errno = (int)-r;
|
||||
return -1;
|
||||
}
|
||||
return (int)r;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef VLIBC_INTERNAL_TYPES_H
|
||||
#define VLIBC_INTERNAL_TYPES_H
|
||||
|
||||
/*
|
||||
* vlibc — internal scalar types.
|
||||
*
|
||||
* The kernel-ABI-correct scalar types internal code needs before the public
|
||||
* <sys/types.h> exists. Widths follow the x86_64 LP64 model, matching the
|
||||
* kernel's UAPI: pointers and long are 64-bit, int is 32-bit. ssize_t is
|
||||
* derived from the compiler's pointer-difference type so it always matches
|
||||
* the machine word. The public headers own their own (possibly richer)
|
||||
* typedefs; this header is deliberately the minimal internal subset.
|
||||
*/
|
||||
|
||||
/* Signed type for byte counts and read()/write() results (x86_64: long). */
|
||||
typedef __PTRDIFF_TYPE__ ssize_t;
|
||||
|
||||
/* File offset and its Linux large-file counterpart. */
|
||||
typedef long off_t;
|
||||
typedef long long loff_t;
|
||||
|
||||
/* Calendar time in seconds. */
|
||||
typedef long time_t;
|
||||
|
||||
/* Process ID. */
|
||||
typedef int pid_t;
|
||||
|
||||
/* User and group IDs. */
|
||||
typedef unsigned int uid_t;
|
||||
typedef unsigned int gid_t;
|
||||
|
||||
/* File mode bits (and permissions). */
|
||||
typedef unsigned int mode_t;
|
||||
|
||||
/* Socket address lengths. */
|
||||
typedef unsigned int socklen_t;
|
||||
|
||||
/* Microsecond intervals. */
|
||||
typedef unsigned int useconds_t;
|
||||
|
||||
/* Hard link count. */
|
||||
typedef unsigned long long nlink_t;
|
||||
|
||||
/* Device and inode numbers. */
|
||||
typedef unsigned long long dev_t;
|
||||
typedef unsigned long long ino_t;
|
||||
|
||||
/* Block counts and sizes. */
|
||||
typedef long blkcnt_t;
|
||||
typedef long blksize_t;
|
||||
|
||||
#endif /* VLIBC_INTERNAL_TYPES_H */
|
||||
@@ -0,0 +1,951 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#include "../internal/malloc.h"
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* vlibc — the heap allocator (todo 7).
|
||||
*
|
||||
* A first-fit allocator with segregated size bins, a brk-grown heap, and an
|
||||
* mmap path for large blocks. Layout reference: musl's single-file malloc;
|
||||
* this is a clean-room implementation with vlibc's own chunk format.
|
||||
*
|
||||
* Chunk layout (every chunk address is 16-aligned; every chunk size is a
|
||||
* multiple of VLIBC_CHUNK_ALIGN = 32):
|
||||
*
|
||||
* struct chunk { size_t psize, csize; } (16-byte header)
|
||||
* user pointer = chunk + 16
|
||||
*
|
||||
* csize low bits (masked with ~3 when reading sizes):
|
||||
* bit 0 VLIBC_CHUNK_INUSE — chunk is allocated.
|
||||
* bit 1 VLIBC_CHUNK_ALIGNED — chunk is the raw block of an
|
||||
* aligned_alloc-style allocation.
|
||||
* psize low bits (masked with ~7 when reading sizes):
|
||||
* bit 0 — the previous chunk is in use (1) or free (0).
|
||||
* bit 2 VLIBC_CHUNK_MMAP — this chunk lives in its own mmap, psize
|
||||
* holds the mapping length.
|
||||
*
|
||||
* Free chunks keep their doubly-linked-list pointers (next/prev) in the
|
||||
* first 16 bytes of the user area, which is why the minimum chunk is
|
||||
* 16 (header) + 16 (pointers) = 32 bytes. There is never more than one
|
||||
* adjacent pair of free chunks: free() coalesces immediately, and the
|
||||
* top chunk (the free block at the end of the heap) is kept out of the
|
||||
* bins.
|
||||
*
|
||||
* Allocation: requests whose normalized chunk size reaches
|
||||
* VLIBC_MMAP_THRESHOLD (128 KiB) get a private SYS_mmap mapping (freed
|
||||
* with SYS_munmap). Everything else searches the bins first-fit (bins 0-3
|
||||
* hold the exact sizes 32/64/96/128, bins 4-12 hold log-size ranges up to
|
||||
* just below the mmap threshold), then splits the top chunk, growing the
|
||||
* heap with SYS_brk as needed. If brk fails, the request falls back to a
|
||||
* single-chunk mmap.
|
||||
*
|
||||
* The brk heap never shrinks below its start address and releases whole
|
||||
* trailing pages back to the kernel when the top chunk holds more than a
|
||||
* page beyond the minimum chunk. sbrk() is deliberately not provided (see
|
||||
* the plan: it conflicts with this allocator).
|
||||
*
|
||||
* Aligned allocations (alignment > 16): the raw block is a normal chunk
|
||||
* marked VLIBC_CHUNK_ALIGNED; the returned pointer sits somewhere inside
|
||||
* the block with a two-word descriptor directly before it:
|
||||
*
|
||||
* [q-16] = alignment (a power of two, so bit 0 is clear)
|
||||
* [q-8] = raw pointer (the start of the block's user area)
|
||||
*
|
||||
* free() tells the two cases apart by the C_INUSE bit of the word at p-16:
|
||||
* an allocated chunk always has it set, while a descriptor's alignment word
|
||||
* always has it clear. realloc() on an aligned pointer preserves the
|
||||
* alignment (via the descriptor) and malloc_usable_size() reports the raw
|
||||
* block size minus the alignment offset.
|
||||
*
|
||||
* malloc(0) returns a unique minimum-size chunk. realloc(p, 0) frees p and
|
||||
* returns NULL. calloc overflows and aligned_alloc/posix_memalign argument
|
||||
* violations fail with NULL + errno (ENOMEM/EINVAL); posix_memalign returns
|
||||
* the error number instead of setting errno and never modifies *memptr on
|
||||
* failure. Over-requested brk chunks zero themselves when they come from a
|
||||
* fresh page of the heap, which makes calloc cheap in the common case.
|
||||
*
|
||||
* Single-threaded for now: the pthread todo will add locking around the
|
||||
* global allocator state.
|
||||
*/
|
||||
|
||||
#define VLIBC_CHUNK_OVERHEAD 16 /* psize + csize */
|
||||
#define VLIBC_CHUNK_ALIGN 32 /* chunk-size granularity */
|
||||
#define VLIBC_CHUNK_MIN 32 /* smallest chunk: header + 2 pointers */
|
||||
|
||||
#define VLIBC_CHUNK_INUSE 1UL /* csize bit 0 */
|
||||
#define VLIBC_CHUNK_ALIGNED 2UL /* csize bit 1 */
|
||||
#define VLIBC_CHUNK_MMAP 4UL /* psize bit 2 (private mmap block) */
|
||||
|
||||
#define VLIBC_CSIZE_MASK (~(size_t)3)
|
||||
#define VLIBC_PSIZE_MASK (~(size_t)7)
|
||||
|
||||
/* Requests whose normalized chunk size reaches this go straight to mmap. */
|
||||
#define VLIBC_MMAP_THRESHOLD ((size_t)(128 * 1024))
|
||||
|
||||
/* 4 exact-size fast bins (32/64/96/128) + 9 log-range bins. */
|
||||
#define VLIBC_BIN_COUNT 13
|
||||
#define VLIBC_BIN_FAST 4
|
||||
|
||||
/* Largest user request that cannot overflow the chunk-size normalization. */
|
||||
#define VLIBC_CHUNK_MAX_USER ((size_t)-1 - VLIBC_CHUNK_ALIGN - VLIBC_CHUNK_OVERHEAD - 1)
|
||||
|
||||
/* Minimum heap growth in bytes, rounded up to a page by the extender. */
|
||||
#define VLIBC_HEAP_GROW ((size_t)32768)
|
||||
|
||||
struct vlibc_chunk
|
||||
{
|
||||
size_t psize;
|
||||
size_t csize;
|
||||
struct vlibc_chunk *next;
|
||||
struct vlibc_chunk *prev;
|
||||
};
|
||||
|
||||
static struct vlibc_chunk *vlibc_bins[VLIBC_BIN_COUNT];
|
||||
static struct vlibc_chunk *vlibc_top; /* free block at the end of the heap */
|
||||
static uintptr_t vlibc_heap_start; /* 0 = heap not yet queried */
|
||||
static uintptr_t vlibc_heap_end; /* current brk */
|
||||
static size_t vlibc_live; /* allocated chunks on the heap */
|
||||
static size_t vlibc_live_mmap; /* allocated mmap chunks */
|
||||
|
||||
static void *
|
||||
vlibc_aligned_core(size_t align, size_t size);
|
||||
|
||||
/* Round a user request up to a chunk size (multiple of VLIBC_CHUNK_ALIGN). */
|
||||
static size_t
|
||||
vlibc_norm(size_t n)
|
||||
{
|
||||
size_t sz =
|
||||
(n + VLIBC_CHUNK_OVERHEAD + VLIBC_CHUNK_ALIGN - 1) & ~(size_t)(VLIBC_CHUNK_ALIGN - 1);
|
||||
|
||||
return sz < VLIBC_CHUNK_MIN ? VLIBC_CHUNK_MIN : sz;
|
||||
}
|
||||
|
||||
/* Index of the bin a free chunk of the given (flag-free) size belongs to. */
|
||||
static int
|
||||
vlibc_bin_of(size_t sz)
|
||||
{
|
||||
if (sz <= (size_t)(VLIBC_BIN_FAST * VLIBC_CHUNK_ALIGN))
|
||||
{
|
||||
return (int)(sz / VLIBC_CHUNK_ALIGN) - 1;
|
||||
}
|
||||
return (int)(VLIBC_BIN_FAST + 63 - (unsigned)__builtin_clzll(sz / VLIBC_CHUNK_ALIGN) - 3);
|
||||
}
|
||||
|
||||
static void
|
||||
vlibc_bin_insert(struct vlibc_chunk *c)
|
||||
{
|
||||
int i = vlibc_bin_of(c->csize & VLIBC_CSIZE_MASK);
|
||||
|
||||
c->prev = NULL;
|
||||
c->next = vlibc_bins[i];
|
||||
if (vlibc_bins[i] != NULL)
|
||||
{
|
||||
vlibc_bins[i]->prev = c;
|
||||
}
|
||||
vlibc_bins[i] = c;
|
||||
}
|
||||
|
||||
static void
|
||||
vlibc_bin_remove(struct vlibc_chunk *c)
|
||||
{
|
||||
int i = vlibc_bin_of(c->csize & VLIBC_CSIZE_MASK);
|
||||
|
||||
if (c->prev != NULL)
|
||||
{
|
||||
c->prev->next = c->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
vlibc_bins[i] = c->next;
|
||||
}
|
||||
if (c->next != NULL)
|
||||
{
|
||||
c->next->prev = c->prev;
|
||||
}
|
||||
c->next = NULL;
|
||||
c->prev = NULL;
|
||||
}
|
||||
|
||||
/* Record c (size sz, flag-free) as allocated and update the next header. */
|
||||
static void
|
||||
vlibc_mark_alloc(struct vlibc_chunk *c, size_t sz)
|
||||
{
|
||||
struct vlibc_chunk *next = (struct vlibc_chunk *)((char *)c + sz);
|
||||
|
||||
c->csize = sz | VLIBC_CHUNK_INUSE;
|
||||
if ((uintptr_t)next < vlibc_heap_end && next != vlibc_top)
|
||||
{
|
||||
next->psize = c->csize;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Query the brk once and remember the heap window. Returns 0 on success;
|
||||
* -1 leaves the window uninitialized so callers fall back to mmap.
|
||||
*/
|
||||
static int
|
||||
vlibc_heap_init(void)
|
||||
{
|
||||
long r;
|
||||
|
||||
if (vlibc_heap_start != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
r = __syscall1(SYS_brk, 0);
|
||||
if (r < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
vlibc_heap_start = ((uintptr_t)r + 15) & ~(uintptr_t)15;
|
||||
vlibc_heap_end = vlibc_heap_start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grow the heap by at least need bytes. On success the top chunk covers the
|
||||
* new space (created if the heap was fully consumed); on failure the state
|
||||
* is unchanged and the caller falls back to a private mmap.
|
||||
*/
|
||||
static int
|
||||
vlibc_heap_extend(size_t need)
|
||||
{
|
||||
size_t want = need > VLIBC_HEAP_GROW ? need : VLIBC_HEAP_GROW;
|
||||
uintptr_t new_end;
|
||||
long r;
|
||||
|
||||
want = (want + 4095) & ~(size_t)4095;
|
||||
new_end = vlibc_heap_end + want;
|
||||
r = __syscall1(SYS_brk, (long)new_end);
|
||||
if ((uintptr_t)r != new_end)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (vlibc_top != NULL)
|
||||
{
|
||||
vlibc_top->csize += want;
|
||||
}
|
||||
else
|
||||
{
|
||||
vlibc_top = (struct vlibc_chunk *)vlibc_heap_end;
|
||||
vlibc_top->psize = 0 | VLIBC_CHUNK_INUSE; /* prev size unknown */
|
||||
vlibc_top->csize = want;
|
||||
}
|
||||
vlibc_heap_end = new_end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Give whole trailing pages of the top chunk back to the kernel. Always
|
||||
* keeps at least a minimum chunk so the heap window never closes.
|
||||
*/
|
||||
static void
|
||||
vlibc_heap_trim(void)
|
||||
{
|
||||
size_t sz = vlibc_top->csize;
|
||||
size_t release = (sz - VLIBC_CHUNK_MIN) & ~(size_t)4095;
|
||||
uintptr_t new_end;
|
||||
long r;
|
||||
|
||||
if (release < 4096)
|
||||
{
|
||||
return;
|
||||
}
|
||||
new_end = (uintptr_t)vlibc_top + sz - release;
|
||||
r = __syscall1(SYS_brk, (long)new_end);
|
||||
if ((uintptr_t)r == new_end)
|
||||
{
|
||||
vlibc_top->csize = sz - release;
|
||||
vlibc_heap_end = new_end;
|
||||
}
|
||||
}
|
||||
|
||||
/* Private mmap block of normalized size nn. NULL on failure. */
|
||||
static void *
|
||||
vlibc_mmap_chunk(size_t nn)
|
||||
{
|
||||
size_t len;
|
||||
long r;
|
||||
struct vlibc_chunk *c;
|
||||
|
||||
if (nn > (size_t)-1 - VLIBC_CHUNK_OVERHEAD - 4095)
|
||||
{
|
||||
return NULL; /* the rounded mapping length would overflow */
|
||||
}
|
||||
len = (nn + VLIBC_CHUNK_OVERHEAD + 4095) & ~(size_t)4095;
|
||||
r = __syscall6(SYS_mmap, 0, (long)len, 3, 0x22, -1, 0);
|
||||
|
||||
if ((uintptr_t)r > (uintptr_t)-4096)
|
||||
{
|
||||
return NULL; /* kernel returned -errno */
|
||||
}
|
||||
c = (struct vlibc_chunk *)r;
|
||||
c->psize = len | VLIBC_CHUNK_MMAP;
|
||||
c->csize = nn | VLIBC_CHUNK_INUSE;
|
||||
vlibc_live_mmap++;
|
||||
return (char *)c + VLIBC_CHUNK_OVERHEAD;
|
||||
}
|
||||
|
||||
/* First free chunk with size >= nn anywhere in bins [bin_of(nn), end). */
|
||||
static struct vlibc_chunk *
|
||||
vlibc_bin_find(size_t nn)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = vlibc_bin_of(nn); i < VLIBC_BIN_COUNT; i++)
|
||||
{
|
||||
struct vlibc_chunk *c;
|
||||
|
||||
for (c = vlibc_bins[i]; c != NULL; c = c->next)
|
||||
{
|
||||
if ((c->csize & VLIBC_CSIZE_MASK) >= nn)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Split free chunk c (current size s) into an nn-byte allocation and a free
|
||||
* remainder. Requires s >= nn + MIN. The chunk after the original extent
|
||||
* gets its psize pointed at the remainder: without that update a later free
|
||||
* there would walk back over the stale (too large) size and land on the
|
||||
* wrong chunk. When the split chunk reaches the top chunk, the remainder is
|
||||
* absorbed into the top instead of entering a bin — a free chunk directly
|
||||
* before top would violate the coalescing invariant and leave the top's
|
||||
* predecessor size stale.
|
||||
*/
|
||||
static void
|
||||
vlibc_split(struct vlibc_chunk *c, size_t s, size_t nn)
|
||||
{
|
||||
struct vlibc_chunk *rem = (struct vlibc_chunk *)((char *)c + nn);
|
||||
struct vlibc_chunk *after = (struct vlibc_chunk *)((char *)c + s);
|
||||
|
||||
rem->psize = nn; /* prev still free at this instant */
|
||||
rem->csize = s - nn;
|
||||
c->csize = nn;
|
||||
if (after == vlibc_top)
|
||||
{
|
||||
rem->csize += vlibc_top->csize;
|
||||
rem->psize = nn | VLIBC_CHUNK_INUSE; /* c becomes allocated below */
|
||||
vlibc_top = rem;
|
||||
}
|
||||
else if (after == (struct vlibc_chunk *)vlibc_heap_end)
|
||||
{
|
||||
rem->psize = nn | VLIBC_CHUNK_INUSE;
|
||||
vlibc_top = rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((uintptr_t)after < vlibc_heap_end)
|
||||
{
|
||||
after->psize = s - nn;
|
||||
}
|
||||
vlibc_bin_insert(rem);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Carve nn bytes out of the top chunk and return the user pointer. When the
|
||||
* remainder is at least a minimum chunk it becomes the new top; otherwise
|
||||
* the whole top is consumed and the next extension starts a fresh one.
|
||||
*/
|
||||
static void *
|
||||
vlibc_carve_top(size_t nn)
|
||||
{
|
||||
struct vlibc_chunk *c = vlibc_top;
|
||||
size_t s = c->csize;
|
||||
|
||||
if (s >= nn + VLIBC_CHUNK_MIN)
|
||||
{
|
||||
struct vlibc_chunk *rem = (struct vlibc_chunk *)((char *)c + nn);
|
||||
|
||||
rem->psize = nn | VLIBC_CHUNK_INUSE;
|
||||
rem->csize = s - nn;
|
||||
c->csize = nn | VLIBC_CHUNK_INUSE;
|
||||
vlibc_top = rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->csize = s | VLIBC_CHUNK_INUSE;
|
||||
vlibc_top = NULL;
|
||||
}
|
||||
vlibc_live++;
|
||||
return (char *)c + VLIBC_CHUNK_OVERHEAD;
|
||||
}
|
||||
|
||||
/*
|
||||
* The allocator core (the internal seam src/internal/malloc.h documents).
|
||||
* Allocates n bytes, 16-byte aligned; NULL + errno ENOMEM on failure. May
|
||||
* return a unique pointer even when n == 0.
|
||||
*/
|
||||
hidden void *
|
||||
__libc_malloc(size_t n) // NOLINT(bugprone-reserved-identifier)
|
||||
{
|
||||
size_t nn;
|
||||
struct vlibc_chunk *c;
|
||||
|
||||
if (n > VLIBC_CHUNK_MAX_USER)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
nn = vlibc_norm(n);
|
||||
if (nn >= VLIBC_MMAP_THRESHOLD)
|
||||
{
|
||||
void *p = vlibc_mmap_chunk(nn);
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
c = vlibc_bin_find(nn);
|
||||
if (c != NULL)
|
||||
{
|
||||
size_t s = c->csize & VLIBC_CSIZE_MASK;
|
||||
|
||||
vlibc_bin_remove(c);
|
||||
if (s >= nn + VLIBC_CHUNK_MIN)
|
||||
{
|
||||
vlibc_split(c, s, nn);
|
||||
vlibc_mark_alloc(c, nn);
|
||||
}
|
||||
else
|
||||
{
|
||||
vlibc_mark_alloc(c, s);
|
||||
}
|
||||
vlibc_live++;
|
||||
return (char *)c + VLIBC_CHUNK_OVERHEAD;
|
||||
}
|
||||
if (vlibc_heap_init() == 0 && vlibc_heap_extend(nn) == 0)
|
||||
{
|
||||
return vlibc_carve_top(nn);
|
||||
}
|
||||
{
|
||||
void *p = vlibc_mmap_chunk(nn);
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Release a block returned by __libc_malloc; NULL is a no-op. Coalesces
|
||||
* with adjacent free chunks and the top chunk, then trims trailing pages.
|
||||
*/
|
||||
hidden void
|
||||
__libc_free(void *p) // NOLINT(bugprone-reserved-identifier)
|
||||
{
|
||||
struct vlibc_chunk *c;
|
||||
size_t sz;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
c = (struct vlibc_chunk *)((char *)p - VLIBC_CHUNK_OVERHEAD);
|
||||
if ((c->csize & VLIBC_CHUNK_INUSE) == 0)
|
||||
{
|
||||
/* Aligned allocation: the descriptor sits right before p. */
|
||||
void *raw = *(void **)((char *)p - sizeof(void *));
|
||||
|
||||
p = raw;
|
||||
c = (struct vlibc_chunk *)((char *)raw - VLIBC_CHUNK_OVERHEAD);
|
||||
}
|
||||
if ((c->psize & VLIBC_CHUNK_MMAP) != 0)
|
||||
{
|
||||
(void)__syscall2(SYS_munmap, (long)c, (long)(c->psize & VLIBC_PSIZE_MASK));
|
||||
vlibc_live_mmap--;
|
||||
return;
|
||||
}
|
||||
vlibc_live--;
|
||||
sz = c->csize & VLIBC_CSIZE_MASK;
|
||||
c->csize = sz; /* clear INUSE and ALIGNED */
|
||||
|
||||
/* Coalesce with the following chunk, top chunk first. */
|
||||
{
|
||||
struct vlibc_chunk *next = (struct vlibc_chunk *)((char *)c + sz);
|
||||
|
||||
if (next == vlibc_top)
|
||||
{
|
||||
c->csize += vlibc_top->csize;
|
||||
vlibc_top = c;
|
||||
}
|
||||
else if ((uintptr_t)next < vlibc_heap_end && (next->csize & VLIBC_CHUNK_INUSE) == 0)
|
||||
{
|
||||
struct vlibc_chunk *after;
|
||||
|
||||
vlibc_bin_remove(next);
|
||||
c->csize += next->csize;
|
||||
after = (struct vlibc_chunk *)((char *)c + c->csize);
|
||||
if ((uintptr_t)after < vlibc_heap_end)
|
||||
{
|
||||
after->psize = c->csize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Coalesce with the preceding chunk when it is free. */
|
||||
if ((c->psize & 1) == 0)
|
||||
{
|
||||
struct vlibc_chunk *prev =
|
||||
(struct vlibc_chunk *)((char *)c - (c->psize & VLIBC_PSIZE_MASK));
|
||||
struct vlibc_chunk *after;
|
||||
|
||||
vlibc_bin_remove(prev);
|
||||
prev->csize += c->csize;
|
||||
if (c == vlibc_top)
|
||||
{
|
||||
vlibc_top = prev;
|
||||
}
|
||||
c = prev;
|
||||
after = (struct vlibc_chunk *)((char *)c + c->csize);
|
||||
if ((uintptr_t)after < vlibc_heap_end)
|
||||
{
|
||||
after->psize = c->csize;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A free block that ends exactly at the heap end is — or joins — the
|
||||
* top chunk. This must also run when the top was fully consumed
|
||||
* (vlibc_top == NULL): a freed trailing chunk that merely entered a bin
|
||||
* would leave the allocator without a top, and a later extension would
|
||||
* then grow over the bin's space while stale top state lets trim shrink
|
||||
* the brk across live chunks.
|
||||
*/
|
||||
{
|
||||
struct vlibc_chunk *end = (struct vlibc_chunk *)((char *)c + (c->csize & VLIBC_CSIZE_MASK));
|
||||
|
||||
if (end == vlibc_top)
|
||||
{
|
||||
c->csize += vlibc_top->csize;
|
||||
vlibc_top = c;
|
||||
}
|
||||
else if (end == (struct vlibc_chunk *)vlibc_heap_end)
|
||||
{
|
||||
vlibc_top = c;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == vlibc_top)
|
||||
{
|
||||
vlibc_heap_trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
vlibc_bin_insert(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The public malloc: thin forward to the internal seam so that every
|
||||
* allocation path in the library shares one implementation.
|
||||
*/
|
||||
void *
|
||||
malloc(size_t n)
|
||||
{
|
||||
return __libc_malloc(n);
|
||||
}
|
||||
|
||||
/* The public free: thin forward to the internal seam. */
|
||||
void
|
||||
free(void *p)
|
||||
{
|
||||
__libc_free(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate zeroed memory for nmemb elements of size bytes. The product is
|
||||
* overflow-checked; NULL + errno ENOMEM on failure. calloc(n, 0) and
|
||||
* calloc(0, n) return a unique zeroed minimum-size chunk like malloc(0).
|
||||
*/
|
||||
void *
|
||||
calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
size_t total;
|
||||
void *p;
|
||||
struct vlibc_chunk *c;
|
||||
size_t usable;
|
||||
size_t i;
|
||||
|
||||
if (size != 0 && nmemb > (size_t)-1 / size)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
total = nmemb * size;
|
||||
p = malloc(total);
|
||||
if (p == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
c = (struct vlibc_chunk *)((char *)p - VLIBC_CHUNK_OVERHEAD);
|
||||
if ((c->psize & VLIBC_CHUNK_MMAP) != 0)
|
||||
{
|
||||
return p; /* a fresh mmap is already zero */
|
||||
}
|
||||
usable = (c->csize & VLIBC_CSIZE_MASK) - VLIBC_CHUNK_OVERHEAD;
|
||||
for (i = 0; i < usable; i++)
|
||||
{
|
||||
((unsigned char *)p)[i] = 0;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize the block at p to n bytes. realloc(NULL, n) is malloc(n);
|
||||
* realloc(p, 0) frees p and returns NULL. Growth first tries to merge the
|
||||
* following free chunk (or the top chunk); otherwise the block is copied to
|
||||
* a fresh allocation and freed. On failure the original block is untouched
|
||||
* and errno is ENOMEM.
|
||||
*/
|
||||
void *
|
||||
realloc(void *p, size_t n)
|
||||
{
|
||||
struct vlibc_chunk *c;
|
||||
size_t sz;
|
||||
size_t nn;
|
||||
void *np;
|
||||
size_t old;
|
||||
size_t copy;
|
||||
size_t i;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
return malloc(n);
|
||||
}
|
||||
if (n == 0)
|
||||
{
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
if (n > VLIBC_CHUNK_MAX_USER)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
nn = vlibc_norm(n);
|
||||
|
||||
c = (struct vlibc_chunk *)((char *)p - VLIBC_CHUNK_OVERHEAD);
|
||||
if ((c->csize & VLIBC_CHUNK_INUSE) == 0)
|
||||
{
|
||||
/* Aligned allocation: re-allocate with the original alignment. */
|
||||
size_t align = *(size_t *)((char *)p - 2 * sizeof(size_t));
|
||||
void *raw = *(void **)((char *)p - sizeof(void *));
|
||||
struct vlibc_chunk *rc = (struct vlibc_chunk *)((char *)raw - VLIBC_CHUNK_OVERHEAD);
|
||||
|
||||
old =
|
||||
(rc->csize & VLIBC_CSIZE_MASK) - VLIBC_CHUNK_OVERHEAD - ((uintptr_t)p - (uintptr_t)raw);
|
||||
if (n % align != 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
np = vlibc_aligned_core(align, n);
|
||||
if (np == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
copy = old < n ? old : n;
|
||||
for (i = 0; i < copy; i++)
|
||||
{
|
||||
((unsigned char *)np)[i] = ((unsigned char *)p)[i];
|
||||
}
|
||||
free(p);
|
||||
return np;
|
||||
}
|
||||
if ((c->psize & VLIBC_CHUNK_MMAP) != 0)
|
||||
{
|
||||
old = (c->csize & VLIBC_CSIZE_MASK) - VLIBC_CHUNK_OVERHEAD;
|
||||
np = malloc(n);
|
||||
if (np == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
copy = old < n ? old : n;
|
||||
for (i = 0; i < copy; i++)
|
||||
{
|
||||
((unsigned char *)np)[i] = ((unsigned char *)p)[i];
|
||||
}
|
||||
free(p);
|
||||
return np;
|
||||
}
|
||||
|
||||
sz = c->csize & VLIBC_CSIZE_MASK;
|
||||
if (nn <= sz)
|
||||
{
|
||||
if (sz >= nn + VLIBC_CHUNK_MIN)
|
||||
{
|
||||
/* Shrink in place, releasing the tail as a free chunk. */
|
||||
vlibc_split(c, sz, nn);
|
||||
vlibc_mark_alloc(c, nn);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Try to grow into the following free chunk (or the top chunk). */
|
||||
{
|
||||
struct vlibc_chunk *next = (struct vlibc_chunk *)((char *)c + sz);
|
||||
|
||||
if (next == vlibc_top)
|
||||
{
|
||||
size_t total = sz + vlibc_top->csize;
|
||||
|
||||
if (total >= nn)
|
||||
{
|
||||
if (total >= nn + VLIBC_CHUNK_MIN)
|
||||
{
|
||||
struct vlibc_chunk *rem = (struct vlibc_chunk *)((char *)c + nn);
|
||||
|
||||
rem->psize = nn | VLIBC_CHUNK_INUSE;
|
||||
rem->csize = total - nn;
|
||||
c->csize = nn | VLIBC_CHUNK_INUSE;
|
||||
vlibc_top = rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->csize = total | VLIBC_CHUNK_INUSE;
|
||||
vlibc_top = NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
else if ((uintptr_t)next < vlibc_heap_end && (next->csize & VLIBC_CHUNK_INUSE) == 0)
|
||||
{
|
||||
size_t total = sz + (next->csize & VLIBC_CSIZE_MASK);
|
||||
|
||||
if (total >= nn)
|
||||
{
|
||||
vlibc_bin_remove(next);
|
||||
if (total >= nn + VLIBC_CHUNK_MIN)
|
||||
{
|
||||
struct vlibc_chunk *rem = (struct vlibc_chunk *)((char *)c + nn);
|
||||
struct vlibc_chunk *after;
|
||||
|
||||
rem->psize = nn | VLIBC_CHUNK_INUSE;
|
||||
rem->csize = total - nn;
|
||||
c->csize = nn | VLIBC_CHUNK_INUSE;
|
||||
after = (struct vlibc_chunk *)((char *)rem + rem->csize);
|
||||
if (after == vlibc_top)
|
||||
{
|
||||
rem->csize += vlibc_top->csize;
|
||||
vlibc_top = rem;
|
||||
}
|
||||
else if (after == (struct vlibc_chunk *)vlibc_heap_end)
|
||||
{
|
||||
vlibc_top = rem;
|
||||
}
|
||||
else
|
||||
{
|
||||
vlibc_bin_insert(rem);
|
||||
if ((uintptr_t)after < vlibc_heap_end)
|
||||
{
|
||||
after->psize = rem->csize;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct vlibc_chunk *after = (struct vlibc_chunk *)((char *)c + total);
|
||||
|
||||
c->csize = total | VLIBC_CHUNK_INUSE;
|
||||
if ((uintptr_t)after < vlibc_heap_end)
|
||||
{
|
||||
after->psize = c->csize;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
/* Keep next linked; fall through to alloc-copy-free. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate fresh, copy the smaller of the two payloads, free the old. */
|
||||
old = sz - VLIBC_CHUNK_OVERHEAD;
|
||||
np = malloc(n);
|
||||
if (np == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
copy = old < n ? old : n;
|
||||
for (i = 0; i < copy; i++)
|
||||
{
|
||||
((unsigned char *)np)[i] = ((unsigned char *)p)[i];
|
||||
}
|
||||
free(p);
|
||||
return np;
|
||||
}
|
||||
|
||||
/*
|
||||
* Core of the aligned family: allocate size bytes aligned to align (a
|
||||
* power of two greater than 16). The returned pointer has a two-word
|
||||
* descriptor directly before it ([q-16] = align, [q-8] = raw pointer).
|
||||
*/
|
||||
static void *
|
||||
vlibc_aligned_core(size_t align, size_t size)
|
||||
{
|
||||
void *raw;
|
||||
void *q;
|
||||
struct vlibc_chunk *c;
|
||||
|
||||
raw = malloc(size + align + VLIBC_CHUNK_ALIGN);
|
||||
if (raw == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
q = (void *)(((uintptr_t)raw + VLIBC_CHUNK_ALIGN + align - 1) & -(uintptr_t)align);
|
||||
*(size_t *)((char *)q - 2 * sizeof(size_t)) = align;
|
||||
*(void **)((char *)q - sizeof(void *)) = raw;
|
||||
c = (struct vlibc_chunk *)((char *)raw - VLIBC_CHUNK_OVERHEAD);
|
||||
c->csize |= VLIBC_CHUNK_ALIGNED;
|
||||
return q;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate size bytes aligned to alignment. alignment must be a power of
|
||||
* two that is a multiple of sizeof(void *), and size must be a multiple of
|
||||
* alignment; a violation fails with NULL + errno EINVAL (a non-power-of-two
|
||||
* alignment is undefined behavior in C23, so only well-formed arguments
|
||||
* reach the allocator). size 0 returns NULL. The returned pointer is a
|
||||
* valid malloc block and is released with free.
|
||||
*/
|
||||
void *
|
||||
aligned_alloc(size_t alignment, size_t size)
|
||||
{
|
||||
if ((alignment & (alignment - 1)) != 0 || alignment % sizeof(void *) != 0 ||
|
||||
size % alignment != 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (alignment <= VLIBC_CHUNK_OVERHEAD)
|
||||
{
|
||||
return malloc(size);
|
||||
}
|
||||
return vlibc_aligned_core(alignment, size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate size bytes at address alignment and store the result in
|
||||
* *memptr. alignment must be a power of two and a multiple of
|
||||
* sizeof(void *). Returns 0 on success, EINVAL for a bad alignment, ENOMEM
|
||||
* on allocation failure; never sets errno itself and never modifies
|
||||
* *memptr on failure. size 0 returns a unique minimum-size block.
|
||||
*/
|
||||
int
|
||||
posix_memalign(void **memptr, size_t alignment, size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (memptr == NULL || (alignment & (alignment - 1)) != 0 || alignment % sizeof(void *) != 0)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
p = vlibc_aligned_core(alignment, size == 0 ? 1 : size);
|
||||
if (p == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
*memptr = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/*
|
||||
* Return the number of bytes actually available in the block at p,
|
||||
* including any internal padding. p may be any block returned by the
|
||||
* allocator family. NULL returns 0.
|
||||
*/
|
||||
size_t
|
||||
malloc_usable_size(void *p)
|
||||
{
|
||||
struct vlibc_chunk *c;
|
||||
|
||||
if (p == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
c = (struct vlibc_chunk *)((char *)p - VLIBC_CHUNK_OVERHEAD);
|
||||
if ((c->csize & VLIBC_CHUNK_INUSE) == 0)
|
||||
{
|
||||
/* Aligned allocation: report the raw block minus the offset. */
|
||||
void *raw = *(void **)((char *)p - sizeof(void *));
|
||||
struct vlibc_chunk *rc = (struct vlibc_chunk *)((char *)raw - VLIBC_CHUNK_OVERHEAD);
|
||||
|
||||
if ((rc->psize & VLIBC_CHUNK_MMAP) != 0)
|
||||
{
|
||||
return (rc->psize & VLIBC_PSIZE_MASK) - VLIBC_CHUNK_OVERHEAD -
|
||||
((uintptr_t)p - (uintptr_t)raw);
|
||||
}
|
||||
return (rc->csize & VLIBC_CSIZE_MASK) - VLIBC_CHUNK_OVERHEAD -
|
||||
((uintptr_t)p - (uintptr_t)raw);
|
||||
}
|
||||
if ((c->psize & VLIBC_CHUNK_MMAP) != 0)
|
||||
{
|
||||
return (c->psize & VLIBC_PSIZE_MASK) - VLIBC_CHUNK_OVERHEAD;
|
||||
}
|
||||
return (c->csize & VLIBC_CSIZE_MASK) - VLIBC_CHUNK_OVERHEAD;
|
||||
}
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/*
|
||||
* Internal consistency hook used by the test suite: walk the whole heap
|
||||
* chunk-by-chunk (an independent account of every live allocation), sum
|
||||
* the in-use chunks, and compare against the allocator's own counters.
|
||||
* Returns the total number of live blocks, or (size_t)-1 when the heap
|
||||
* walk disagrees with the counters (corruption or an accounting bug).
|
||||
*/
|
||||
hidden size_t
|
||||
__vlibc_malloc_check(void) // NOLINT(bugprone-reserved-identifier)
|
||||
{
|
||||
uintptr_t cur;
|
||||
size_t live = 0;
|
||||
|
||||
if (vlibc_heap_start == 0)
|
||||
{
|
||||
return vlibc_live + vlibc_live_mmap == 0 ? 0 : (size_t)-1;
|
||||
}
|
||||
cur = vlibc_heap_start;
|
||||
while (cur + VLIBC_CHUNK_MIN <= vlibc_heap_end)
|
||||
{
|
||||
struct vlibc_chunk *c = (struct vlibc_chunk *)cur;
|
||||
size_t sz = c->csize & VLIBC_CSIZE_MASK;
|
||||
|
||||
if (sz < VLIBC_CHUNK_MIN || sz % VLIBC_CHUNK_ALIGN != 0 || cur + sz > vlibc_heap_end)
|
||||
{
|
||||
return (size_t)-1;
|
||||
}
|
||||
if ((c->csize & VLIBC_CHUNK_INUSE) != 0)
|
||||
{
|
||||
live++;
|
||||
}
|
||||
cur += sz;
|
||||
}
|
||||
if (cur != vlibc_heap_end || live != vlibc_live)
|
||||
{
|
||||
return (size_t)-1;
|
||||
}
|
||||
return live + vlibc_live_mmap;
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#define SET_EDOM() (errno = EDOM)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#define SET_EDOM() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* cbrt(x) = cube root of x, after musl src/math/cbrt.c (FreeBSD msun
|
||||
* s_cbrt.c, Bruce D. Evans): a 5-bit bit-hack estimate, a degree-4
|
||||
* polynomial refinement to 23 bits, and one Newton step to 53 bits. */
|
||||
static const unsigned int B1 = 715094163; /* B1 = (1023-1023/3-0.03306235651)*2**20 */
|
||||
static const unsigned int B2 = 696219795; /* B2 = (1023-1023/3-54/3-0.03306235651)*2**20 */
|
||||
|
||||
/* |1/cbrt(x) - p(x)| < 2**-23.5 (~[-7.93e-8, 7.929e-8]). */
|
||||
static const double P0 = 1.87595182427177009643; /* 0x3ffe03e60f61e692 */
|
||||
static const double P1 = -1.88497979543377169875; /* 0xbffe28e092f02420 */
|
||||
static const double P2 = 1.621429720105354466140; /* 0x3ff9f1604a49d6c2 */
|
||||
static const double P3 = -0.758397934778766047437; /* 0xbfe844cbbee751d9 */
|
||||
static const double P4 = 0.145996192886612446982; /* 0x3fc2b000d4e4edd7 */
|
||||
|
||||
double
|
||||
cbrt(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
double r;
|
||||
double s;
|
||||
double t;
|
||||
double w;
|
||||
unsigned int hx;
|
||||
|
||||
u.f = x;
|
||||
hx = (unsigned int)(u.i >> 32) & 0x7fffffffU;
|
||||
if (hx >= 0x7ff00000U)
|
||||
return x + x; /* cbrt(NaN,Inf) is itself */
|
||||
|
||||
/* rough cbrt to 5 bits */
|
||||
if (hx < 0x00100000U)
|
||||
{ /* zero or subnormal? */
|
||||
u.f = x * 0x1p54;
|
||||
hx = (unsigned int)(u.i >> 32) & 0x7fffffffU;
|
||||
if (hx == 0)
|
||||
return x; /* cbrt(0) is itself */
|
||||
hx = hx / 3 + B2;
|
||||
}
|
||||
else
|
||||
hx = hx / 3 + B1;
|
||||
u.i &= 1ULL << 63;
|
||||
u.i |= (unsigned long long)hx << 32;
|
||||
t = u.f;
|
||||
|
||||
/* new cbrt to 23 bits */
|
||||
r = (t * t) * (t / x);
|
||||
t = t * ((P0 + r * (P1 + r * P2)) + ((r * r) * r) * (P3 + r * P4));
|
||||
|
||||
/* round t away from zero to 23 bits, larger in magnitude than cbrt(x) */
|
||||
u.f = t;
|
||||
u.i = (u.i + 0x80000000ULL) & 0xffffffffc0000000ULL;
|
||||
t = u.f;
|
||||
|
||||
/* one Newton iteration to 53 bits with error < 0.667 ulps */
|
||||
s = t * t; /* t*t is exact */
|
||||
r = x / s; /* error <= 0.5 ulps; |r| < |t| */
|
||||
w = t + t; /* t+t is exact */
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* cbrtf(x) = cube root of x, after musl src/math/cbrtf.c: the 5-bit
|
||||
* bit-hack plus two Newton steps evaluated in double precision. */
|
||||
static const unsigned int B1f = 709958130; /* B1f = (127-127.0/3-0.03306235651)*2**23 */
|
||||
static const unsigned int B2f = 642849266; /* B2f = (127-127.0/3-24/3-0.03306235651)*2**23 */
|
||||
|
||||
float
|
||||
cbrtf(float x)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} u;
|
||||
double r;
|
||||
double T;
|
||||
unsigned int hx;
|
||||
|
||||
u.f = x;
|
||||
hx = u.i & 0x7fffffffU;
|
||||
if (hx >= 0x7f800000U)
|
||||
return x + x; /* cbrt(NaN,Inf) is itself */
|
||||
|
||||
/* rough cbrt to 5 bits */
|
||||
if (hx < 0x00800000U)
|
||||
{ /* zero or subnormal? */
|
||||
if (hx == 0)
|
||||
return x; /* cbrt(+-0) is itself */
|
||||
u.f = x * 0x1p24f;
|
||||
hx = u.i & 0x7fffffffU;
|
||||
hx = hx / 3 + B2f;
|
||||
}
|
||||
else
|
||||
hx = hx / 3 + B1f;
|
||||
u.i &= 0x80000000U;
|
||||
u.i |= hx;
|
||||
|
||||
/* first Newton step (solving t*t - x/t == 0) to 16 bits, in double */
|
||||
T = u.f;
|
||||
r = T * T * T;
|
||||
T = T * ((double)x + x + r) / (x + r + r);
|
||||
|
||||
/* second Newton step to 47 bits; rounding to 24 bits is exact in
|
||||
* round-to-nearest mode */
|
||||
r = T * T * T;
|
||||
T = T * ((double)x + x + r) / (x + r + r);
|
||||
return (float)T;
|
||||
}
|
||||
|
||||
/* cbrtl(x) = cube root of x, after musl src/math/cbrtl.c (ld80 branch,
|
||||
* S. Kargl / B. Evans / D. Schultz): reduce x to [1,8) via e%3 and run the
|
||||
* cbrtf-style estimates in double, then one long-double Newton step. */
|
||||
static const unsigned int B1l = 709958130; /* B1l = (127-127.0/3-0.03306235651)*2**23 */
|
||||
|
||||
long double
|
||||
cbrtl(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} v;
|
||||
union
|
||||
{
|
||||
float f;
|
||||
unsigned int i;
|
||||
} uft;
|
||||
long double r;
|
||||
long double s;
|
||||
long double t;
|
||||
long double w;
|
||||
double dr;
|
||||
double dt;
|
||||
double dx;
|
||||
float ft;
|
||||
int e;
|
||||
int sign;
|
||||
|
||||
u.f = x;
|
||||
e = u.p.se & 0x7fff;
|
||||
sign = u.p.se & 0x8000;
|
||||
if (e == 0x7fff)
|
||||
return x + x; /* cbrt(+-Inf) = +-Inf, cbrt(NaN) = NaN */
|
||||
if (e == 0)
|
||||
{ /* adjust subnormal numbers */
|
||||
u.f *= 0x1p120L;
|
||||
e = u.p.se & 0x7fff;
|
||||
if (e == 0)
|
||||
return x; /* cbrt(+-0) = +-0 */
|
||||
e -= 120;
|
||||
}
|
||||
e -= 0x3fff;
|
||||
u.p.se = 0x3fff;
|
||||
x = u.f;
|
||||
switch (e % 3)
|
||||
{
|
||||
case 1:
|
||||
case -2:
|
||||
x *= 2;
|
||||
e--;
|
||||
break;
|
||||
case 2:
|
||||
case -1:
|
||||
x *= 4;
|
||||
e -= 2;
|
||||
break;
|
||||
}
|
||||
v.f = 1.0L;
|
||||
v.p.se = (unsigned short)(sign | (0x3fff + e / 3));
|
||||
|
||||
/* ~5-bit estimate */
|
||||
uft.f = x;
|
||||
uft.i = (uft.i & 0x7fffffffU) / 3 + B1l;
|
||||
ft = uft.f;
|
||||
|
||||
/* ~16-bit estimate */
|
||||
dx = x;
|
||||
dt = ft;
|
||||
dr = dt * dt * dt;
|
||||
dt = dt * (dx + dx + dr) / (dx + dr + dr);
|
||||
|
||||
/* ~47-bit estimate */
|
||||
dr = dt * dt * dt;
|
||||
dt = dt * (dx + dx + dr) / (dx + dr + dr);
|
||||
|
||||
/* round dt away from zero to 32 bits so t*t is exact */
|
||||
t = dt + (0x1.0p32L + 0x1.0p-31L) - 0x1.0p32;
|
||||
|
||||
/* final Newton iteration to 64 bits with error < 0.667 ulps */
|
||||
s = t * t; /* t*t is exact */
|
||||
r = x / s; /* error <= 0.5 ulps; |r| < |t| */
|
||||
w = t + t; /* t+t is exact */
|
||||
r = (r - t) / (w + r);
|
||||
t = t + t * r;
|
||||
t *= v.f;
|
||||
return t;
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Smallest integral value not less than x (C23 7.12.9.1), all three
|
||||
* precisions.
|
||||
*
|
||||
* As with floor (see floor.c), GCC does not fold the __builtin_ceil forms
|
||||
* on this target, so each function works on the IEEE 754 bit pattern.
|
||||
* Clearing the fraction bits rounds toward zero, which is already the
|
||||
* correct direction for a negative argument; a positive argument with a
|
||||
* nonzero fraction must then step up by one (an exact integer + 1.0).
|
||||
* ceil(-0.3) is a real -0.0, and ±0/±Inf/NaN pass through unchanged.
|
||||
*/
|
||||
|
||||
static double
|
||||
ceil_d(double x)
|
||||
{
|
||||
const unsigned long long sign_mask = 1ULL << 63;
|
||||
unsigned long long bits;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 52) & 0x7ff);
|
||||
|
||||
if (e >= 1075)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 1023)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -0.0 : 1.0;
|
||||
}
|
||||
|
||||
shift = 1075 - e;
|
||||
frac = (1ULL << shift) - 1;
|
||||
if ((bits & sign_mask) != 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x + 1.0;
|
||||
}
|
||||
|
||||
static float
|
||||
ceil_f(float x)
|
||||
{
|
||||
const unsigned int sign_mask = 1U << 31;
|
||||
unsigned int bits;
|
||||
unsigned int frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&bits, &x, sizeof bits);
|
||||
e = (int)((bits >> 23) & 0xff);
|
||||
|
||||
if (e >= 150)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 127)
|
||||
{
|
||||
if ((bits & ~sign_mask) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (bits & sign_mask) != 0 ? -0.0f : 1.0f;
|
||||
}
|
||||
|
||||
shift = 150 - e;
|
||||
frac = (1U << shift) - 1U;
|
||||
if ((bits & sign_mask) != 0)
|
||||
{
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x;
|
||||
}
|
||||
if ((bits & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
bits &= ~frac;
|
||||
__builtin_memcpy(&x, &bits, sizeof x);
|
||||
return x + 1.0f;
|
||||
}
|
||||
|
||||
static long double
|
||||
ceil_ld(long double x)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
unsigned long long frac;
|
||||
int e;
|
||||
int shift;
|
||||
|
||||
__builtin_memcpy(&p, &x, sizeof p);
|
||||
e = p.se & 0x7fff;
|
||||
|
||||
if (e >= 16446)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
if (e < 16383)
|
||||
{
|
||||
if (p.m == 0 && (p.se & 0x7fff) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
return (p.se & 0x8000) != 0 ? -0.0L : 1.0L;
|
||||
}
|
||||
|
||||
shift = 63 - (e - 16383);
|
||||
frac = (1ULL << shift) - 1ULL;
|
||||
if ((p.se & 0x8000) != 0)
|
||||
{
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x;
|
||||
}
|
||||
if ((p.m & frac) == 0)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
p.m &= ~frac;
|
||||
__builtin_memcpy(&x, &p, sizeof p);
|
||||
return x + 1.0L;
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a float argument.
|
||||
*/
|
||||
float
|
||||
ceilf(float x)
|
||||
{
|
||||
return ceil_f(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a double argument.
|
||||
*/
|
||||
double
|
||||
ceil(double x)
|
||||
{
|
||||
return ceil_d(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* As ceil, for a long double argument.
|
||||
*/
|
||||
long double
|
||||
ceill(long double x)
|
||||
{
|
||||
return ceil_ld(x);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Copy the sign of y onto the magnitude of x, all three precisions (C23
|
||||
* 7.12.7.3). Each function below splices the sign bit of y into the IEEE
|
||||
* 754 bit pattern of x: the magnitude of x is untouched (so a NaN keeps
|
||||
* its payload) and copysign(±0, y) carries y's sign. GCC's
|
||||
* __builtin_copysign forms do fold to andp/orp pairs on this target, but
|
||||
* the compiler diagnoses the __builtin_ call inside the identically named
|
||||
* function as infinite recursion, so the splice is written out directly;
|
||||
* it compiles to the same two instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A float with the magnitude of x and the sign of y.
|
||||
*/
|
||||
float
|
||||
copysignf(float x, float y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned int xb;
|
||||
unsigned int yb;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
xb = (xb & 0x7fffffffU) | (yb & 0x80000000U);
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* A double with the magnitude of x and the sign of y.
|
||||
*/
|
||||
double
|
||||
copysign(double x, double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
unsigned long long xb;
|
||||
unsigned long long yb;
|
||||
|
||||
__builtin_memcpy(&xb, &x, sizeof xb);
|
||||
__builtin_memcpy(&yb, &y, sizeof yb);
|
||||
xb = (xb & 0x7fffffffffffffffULL) | (yb & 0x8000000000000000ULL);
|
||||
__builtin_memcpy(&x, &xb, sizeof xb);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* A long double with the magnitude of x and the sign of y. The x86 80-bit
|
||||
* extended format keeps the sign in bit 15 of the sign/exponent word at
|
||||
* bytes 8..9, so only that bit is spliced.
|
||||
*/
|
||||
long double
|
||||
copysignl(long double x, long double y) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} xp;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} yp;
|
||||
|
||||
__builtin_memcpy(&xp, &x, sizeof xp);
|
||||
__builtin_memcpy(&yp, &y, sizeof yp);
|
||||
xp.se = (unsigned short)((xp.se & 0x7fff) | (yp.se & 0x8000));
|
||||
__builtin_memcpy(&x, &xp, sizeof xp);
|
||||
return x;
|
||||
}
|
||||
+661
@@ -0,0 +1,661 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#define SET_ERANGE() (errno = ERANGE)
|
||||
#else
|
||||
#define SET_ERANGE() \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static const double ln2hi = 6.93147180369123816490e-01; /* 0x3fe62e42fee00000 */
|
||||
static const double ln2lo = 1.90821492927058770002e-10; /* 0x3dea39ef35793c76 */
|
||||
static const double invln2 = 1.44269504088896338700e+00; /* 0x3ff71547652b82fe */
|
||||
static const double P1 = 1.66666666666666019037e-01; /* 0x3FC555555555553E */
|
||||
static const double P2 = -2.77777777770155933842e-03; /* 0xBF66C16C16BEBD93 */
|
||||
static const double P3 = 6.61375632143793436117e-05; /* 0x3F11566AAF25DE2C */
|
||||
static const double P4 = -1.65339022054652515390e-06; /* 0xBEBBBD41C5D26BF1 */
|
||||
static const double P5 = 4.13813679705723846039e-08; /* 0x3E66376972BEA4D0 */
|
||||
static const double o_threshold = 709.782712893384;
|
||||
static const double u_threshold = -745.133219101941;
|
||||
static const double twom1000 = 9.33263618503218878990e-302;
|
||||
static const double tiny = 1.0e-300;
|
||||
|
||||
static double
|
||||
vlibc_exp_core(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
unsigned long long sign = u.i >> 63;
|
||||
unsigned int hx = (unsigned int)((u.i >> 32) & 0x7fffffffULL);
|
||||
|
||||
/* NaN or +/-Inf: exp(-inf)=+0, exp(+inf)=+inf, exp(NaN)=NaN */
|
||||
if ((u.i & 0x7fffffffffffffffULL) >= 0x7ff0000000000000ULL)
|
||||
{
|
||||
if ((u.i & 0x000fffffffffffffULL) != 0ULL)
|
||||
return x + x; /* NaN */
|
||||
return sign != 0ULL ? 0.0 : __builtin_huge_val();
|
||||
}
|
||||
|
||||
if (x > o_threshold)
|
||||
return __builtin_huge_val(); /* caller marks ERANGE */
|
||||
if (x < u_threshold)
|
||||
return 0.0; /* caller marks ERANGE */
|
||||
|
||||
double hi = 0.0;
|
||||
double lo = 0.0;
|
||||
int k = 0;
|
||||
|
||||
if (hx > 0x3fd62e42U)
|
||||
{ /* |x| > 0.5 ln2 */
|
||||
if (hx < 0x3ff0a2b2U)
|
||||
{ /* |x| < 1.5 ln2 */
|
||||
if (sign == 0ULL)
|
||||
{
|
||||
hi = x - ln2hi;
|
||||
lo = ln2lo;
|
||||
k = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = x + ln2hi;
|
||||
lo = -ln2lo;
|
||||
k = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (int)(invln2 * x + (sign != 0ULL ? -0.5 : 0.5));
|
||||
double t = (double)k;
|
||||
hi = x - t * ln2hi;
|
||||
lo = t * ln2lo;
|
||||
}
|
||||
x = hi - lo;
|
||||
}
|
||||
else if (hx < 0x3e300000U)
|
||||
{ /* |x| < 2^-28: exp(x) ~ 1+x */
|
||||
(void)tiny;
|
||||
return 1.0 + x;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
|
||||
double t = x * x;
|
||||
double c = x - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5))));
|
||||
|
||||
if (k == 0)
|
||||
return 1.0 - ((x * c) / (c - 2.0) - x);
|
||||
|
||||
double y = 1.0 - ((lo - (x * c) / (2.0 - c)) - hi);
|
||||
|
||||
/* scale y by 2^k by adding k to the exponent field */
|
||||
if (k >= -1021 && k <= 1023)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)k << 52;
|
||||
return b.f;
|
||||
}
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023;
|
||||
/* k <= -1022: scale up into the normal range, then multiply down */
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000;
|
||||
}
|
||||
|
||||
double
|
||||
exp(double x)
|
||||
{
|
||||
if (x > o_threshold)
|
||||
SET_ERANGE();
|
||||
else if (x < u_threshold)
|
||||
SET_ERANGE();
|
||||
return vlibc_exp_core(x);
|
||||
}
|
||||
|
||||
#ifndef exp
|
||||
double
|
||||
exp(double);
|
||||
#endif
|
||||
|
||||
double
|
||||
exp2(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
|
||||
/* NaN or +/-Inf: 2^(-inf)=+0, 2^(+inf)=+inf, 2^(NaN)=NaN */
|
||||
if ((u.i & 0x7fffffffffffffffULL) >= 0x7ff0000000000000ULL)
|
||||
{
|
||||
if ((u.i & 0x000fffffffffffffULL) != 0ULL)
|
||||
return x; /* NaN */
|
||||
return (u.i >> 63) != 0ULL ? 0.0 : __builtin_huge_val();
|
||||
}
|
||||
|
||||
if (x >= 1024.0)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_val();
|
||||
}
|
||||
if (x <= -1075.0)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* round to nearest integer; |x| < 1024 so the conversion is safe */
|
||||
long long k = (long long)(x + (x > 0.0 ? 0.5 : -0.5));
|
||||
/* exact by Sterbenz: k is within 2^52 of x */
|
||||
double r = x - (double)k;
|
||||
/* e^(r ln2) = 2^r */
|
||||
double y = vlibc_exp_core(r * 0.6931471805599453);
|
||||
|
||||
/* scale y by 2^k by adding k to the exponent field */
|
||||
if (k >= -1021 && k <= 1023)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)k << 52;
|
||||
return b.f;
|
||||
}
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023;
|
||||
/* k <= -1022: scale up into the normal range, then multiply down */
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
b.f = y;
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000;
|
||||
}
|
||||
|
||||
float
|
||||
exp2f(float x)
|
||||
{
|
||||
if (x >= 128.0f)
|
||||
return __builtin_huge_valf();
|
||||
if (x <= -150.0f)
|
||||
return 0.0f;
|
||||
return (float)exp2((double)x);
|
||||
}
|
||||
|
||||
long double
|
||||
exp2l(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0 ? 0.0L : x; /* +/-Inf */
|
||||
}
|
||||
|
||||
if (x >= 16384.0L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x <= -16445.0L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0L;
|
||||
}
|
||||
|
||||
/* round to nearest integer; |x| < 16384 so the conversion is safe */
|
||||
long long k = (long long)(x + (x > 0.0L ? 0.5L : -0.5L));
|
||||
/* exact by Sterbenz: k is within 2^63 of x */
|
||||
long double r = x - (long double)k;
|
||||
long double y = r * 0.693147180559945309417232121458176568L;
|
||||
|
||||
/* e^y, |y| <= ln2/2: Taylor series with compensated summation */
|
||||
long double s;
|
||||
if (y > -1e-8L && y < 1e-8L)
|
||||
s = 1.0L + y;
|
||||
else
|
||||
{
|
||||
long double term = y;
|
||||
long double comp = 0.0L;
|
||||
s = 1.0L + y;
|
||||
for (int i = 2; i <= 60; i++)
|
||||
{
|
||||
term *= y / (long double)i;
|
||||
long double yk = term - comp;
|
||||
long double t = s + yk;
|
||||
comp = (t - s) - yk;
|
||||
s = t;
|
||||
if (term > -1e-30L && term < 1e-30L)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (k < -16445)
|
||||
return x > 0.0L ? 0.0L : -0.0L; /* unreachable; defensive */
|
||||
|
||||
/* scale s by 2^k: construct 2^k in long double and multiply */
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} two;
|
||||
|
||||
if (k >= -16382 && k <= 16383)
|
||||
{
|
||||
two.p.m = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + (int)k);
|
||||
return s * two.f;
|
||||
}
|
||||
if (k == 16384)
|
||||
{
|
||||
/* x in [16383.5, 16384): s = 2^r with r in [-0.5, 0) < 1, so
|
||||
* s * 2^16383 * 2 stays finite */
|
||||
two.p.m = 0x8000000000000000ULL;
|
||||
two.p.se = (unsigned short)(16383 + 16383);
|
||||
return s * two.f * 2.0L;
|
||||
}
|
||||
/* k in [-16445, -16383]: 2^k is a pseudo-denormal, int bit clear */
|
||||
two.p.m = 1ULL << (unsigned int)(k + 16445);
|
||||
two.p.se = 0;
|
||||
return s * two.f;
|
||||
}
|
||||
|
||||
static const double Q1 = -3.33333333333331316428e-02; /* 0xbfa1111111110f54 */
|
||||
static const double Q2 = 1.58730158725481460165e-03; /* 0x3f5a01a019fe5585 */
|
||||
static const double Q3 = -7.93650757867487942473e-05; /* 0xbf14ce199eaadbb7 */
|
||||
static const double Q4 = 4.00821782732936260352e-06; /* 0x3ed0cfca86e65239 */
|
||||
static const double Q5 = -2.01099218183624371326e-07; /* 0xbe8afdb76e09c32d */
|
||||
|
||||
/* expm1(x) = e^x - 1, accurate for small x (fdlibm s_expm1). */
|
||||
double
|
||||
expm1(double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} u;
|
||||
double hi = 0.0;
|
||||
double lo = 0.0;
|
||||
double c = 0.0;
|
||||
unsigned int sign;
|
||||
unsigned int hx;
|
||||
int k = 0;
|
||||
|
||||
u.f = x;
|
||||
sign = (unsigned int)(u.i >> 63);
|
||||
hx = (unsigned int)((u.i >> 32) & 0x7fffffffULL);
|
||||
|
||||
/* filter out huge and non-finite argument */
|
||||
if (hx >= 0x7ff00000U)
|
||||
{
|
||||
if ((u.i & 0xfffffffffffffULL) != 0ULL)
|
||||
return x; /* NaN */
|
||||
return sign != 0U ? -1.0 : x; /* expm1(+-inf) = {-1, +inf} */
|
||||
}
|
||||
if (x == 0.0)
|
||||
return x; /* preserve -0.0 */
|
||||
|
||||
if (x > o_threshold)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_val(); /* overflow */
|
||||
}
|
||||
if (sign != 0U && hx >= 0x4043687AU)
|
||||
return -1.0; /* x < -56*ln2, exp(x) < 2^-56 -> -1.0 */
|
||||
|
||||
/* argument reduction */
|
||||
if (hx > 0x3fd62e42U)
|
||||
{
|
||||
if (hx < 0x3ff0a2b2U)
|
||||
{
|
||||
if (sign == 0U)
|
||||
{
|
||||
hi = x - ln2hi;
|
||||
lo = ln2lo;
|
||||
k = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = x + ln2hi;
|
||||
lo = -ln2lo;
|
||||
k = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
k = (int)(invln2 * x + (sign != 0U ? -0.5 : 0.5));
|
||||
{
|
||||
double t = (double)k;
|
||||
hi = x - t * ln2hi; /* t*ln2hi is exact */
|
||||
lo = t * ln2lo;
|
||||
}
|
||||
}
|
||||
x = hi - lo;
|
||||
c = (hi - x) - lo;
|
||||
}
|
||||
else if (hx < 0x3c900000U)
|
||||
{
|
||||
return x; /* |x| < 2^-54: expm1(x) == x */
|
||||
}
|
||||
else
|
||||
{
|
||||
k = 0;
|
||||
}
|
||||
|
||||
/* x is now in primary range */
|
||||
{
|
||||
double hfx = 0.5 * x;
|
||||
double hxs = x * hfx;
|
||||
double r1 = 1.0 + hxs * (Q1 + hxs * (Q2 + hxs * (Q3 + hxs * (Q4 + hxs * Q5))));
|
||||
double t = 3.0 - r1 * hfx;
|
||||
double e = hxs * ((r1 - t) / (6.0 - x * t));
|
||||
|
||||
if (k == 0)
|
||||
return x - (x * e - hxs); /* c is 0 */
|
||||
|
||||
e = (x * (e - c) - c) - hxs;
|
||||
if (k == -1)
|
||||
return 0.5 * (x - e) - 0.5;
|
||||
if (k == 1)
|
||||
{
|
||||
if (x < -0.25)
|
||||
return -2.0 * (e - (x + 0.5));
|
||||
return 1.0 + 2.0 * (x - e);
|
||||
}
|
||||
/* general: expm1(x) = 2^k * (1 - (e - x)) - 1 */
|
||||
{
|
||||
double y = 1.0 - (e - x);
|
||||
union
|
||||
{
|
||||
double f;
|
||||
unsigned long long i;
|
||||
} b;
|
||||
if (k == 1024)
|
||||
return y * 2.0 * 0x1p1023 - 1.0;
|
||||
b.f = y;
|
||||
if (k >= -1021)
|
||||
b.i += (unsigned long long)k << 52;
|
||||
else
|
||||
{
|
||||
b.i += (unsigned long long)(k + 1000) << 52;
|
||||
return b.f * twom1000 - 1.0;
|
||||
}
|
||||
return b.f - 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
expm1f(float x)
|
||||
{
|
||||
if (x >= 88.72283905206835f && !isinf(x))
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_valf(); /* overflow */
|
||||
}
|
||||
return (float)expm1((double)x);
|
||||
}
|
||||
|
||||
/* 80-bit x87 expm1l */
|
||||
long double
|
||||
expm1l(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
long double r;
|
||||
long double sum;
|
||||
long long k;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0U ? -1.0L : x; /* expm1(+-inf) = {-1, +inf} */
|
||||
}
|
||||
if (x == 0.0L)
|
||||
return x;
|
||||
|
||||
if (x > 11356.523406294143L)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x < -16447.0L)
|
||||
return -1.0L;
|
||||
|
||||
/* 2^x = 2^k * e^r with r in [-ln2/2, ln2/2]; ln2 split hi/lo so that
|
||||
* k*ln2hi is exact and r keeps full 64-bit precision */
|
||||
{
|
||||
const long double ln2hi_l = 6.93147180369123816490e-01L;
|
||||
const long double ln2lo_l = 1.90821492927058770002e-10L;
|
||||
const long double invln2l = 1.442695040888963407359924681001892137L;
|
||||
k = (long long)(x * invln2l + (x > 0.0L ? 0.5L : -0.5L));
|
||||
r = x - (long double)k * ln2hi_l - (long double)k * ln2lo_l;
|
||||
}
|
||||
|
||||
/* expm1(r) = e^r - 1 via Kahan-compensated Taylor sum */
|
||||
{
|
||||
long double term = r;
|
||||
long double comp = 0.0L;
|
||||
int i;
|
||||
sum = r;
|
||||
for (i = 2; i <= 60; i++)
|
||||
{
|
||||
long double yt;
|
||||
long double t;
|
||||
term *= r / (long double)i;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
yt = term - comp;
|
||||
t = sum + yt;
|
||||
comp = (t - sum) - yt;
|
||||
sum = t;
|
||||
}
|
||||
}
|
||||
|
||||
if (k == 0)
|
||||
return sum;
|
||||
|
||||
/* scale (sum + 1) by 2^k and subtract 1 */
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} b;
|
||||
long double z = sum + 1.0L;
|
||||
|
||||
if (k >= 16384)
|
||||
return z * 2.0L * 0x1p16383L - 1.0L;
|
||||
if (k >= -16382)
|
||||
{
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k);
|
||||
return z * b.f - 1.0L;
|
||||
}
|
||||
/* k in [-16445, -16383]: e^x < 2^-16382, so e^x - 1 rounds to -1 */
|
||||
return -1.0L;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
expf(float x)
|
||||
{
|
||||
if (isinf(x))
|
||||
return x > 0.0f ? x : 0.0f; /* exp(+inf) = +inf, exp(-inf) = +0 */
|
||||
if (x >= 88.72283905206835f)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_valf(); /* overflow */
|
||||
}
|
||||
if (x < -103.97207708219999f)
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0f; /* underflow to zero */
|
||||
}
|
||||
{
|
||||
double y = exp((double)x);
|
||||
if (y < 0x1p-149) /* glibc flags ERANGE below FLT_TRUE_MIN */
|
||||
SET_ERANGE();
|
||||
return (float)y;
|
||||
}
|
||||
}
|
||||
|
||||
/* 80-bit x87 expl: exp(x) = 2^k * e^r, r in [-ln2/2, ln2/2] */
|
||||
long double
|
||||
expl(long double x)
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} u;
|
||||
long double r;
|
||||
long double sum;
|
||||
long long k;
|
||||
|
||||
u.f = x;
|
||||
if ((u.p.se & 0x7fffU) == 0x7fffU)
|
||||
{
|
||||
if (u.p.m != 0x8000000000000000ULL)
|
||||
return x; /* NaN */
|
||||
return (u.p.se & 0x8000U) != 0U ? 0.0L : x; /* exp(+-inf) = {+0, inf} */
|
||||
}
|
||||
if (x == 0.0L)
|
||||
return 1.0L;
|
||||
|
||||
if (x >= 0xb.17217f7d1cf79acp+10L) /* first 80-bit x where e^x rounds to +inf */
|
||||
{
|
||||
SET_ERANGE();
|
||||
return __builtin_huge_vall();
|
||||
}
|
||||
if (x <= -11399.8484L) /* exp(x) < 2^-16446: rounds to +0 */
|
||||
{
|
||||
SET_ERANGE();
|
||||
return 0.0L;
|
||||
}
|
||||
|
||||
/* ln2 split so k*ln2hi is exact (15-bit hi) and r keeps full
|
||||
* 64-bit precision; k fits long long since |x| < 11356.6 */
|
||||
{
|
||||
const long double ln2hi_l = 6.9314575195312500000000e-1L;
|
||||
const long double ln2lo_l = 1.4286068203094172321214581765680755e-6L;
|
||||
const long double invln2l = 1.442695040888963407359924681001892137L;
|
||||
k = (long long)(x * invln2l + (x > 0.0L ? 0.5L : -0.5L));
|
||||
r = x - (long double)k * ln2hi_l;
|
||||
r -= (long double)k * ln2lo_l;
|
||||
}
|
||||
|
||||
/* e^r via Kahan-compensated Taylor */
|
||||
{
|
||||
long double term = r;
|
||||
long double comp = 0.0L;
|
||||
int i;
|
||||
sum = 1.0L + r;
|
||||
for (i = 2; i <= 60; i++)
|
||||
{
|
||||
long double yt;
|
||||
long double t;
|
||||
term *= r / (long double)i;
|
||||
if (term == 0.0L)
|
||||
break;
|
||||
yt = term - comp;
|
||||
t = sum + yt;
|
||||
comp = (t - sum) - yt;
|
||||
sum = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* scale sum by 2^k */
|
||||
{
|
||||
union
|
||||
{
|
||||
long double f;
|
||||
struct
|
||||
{
|
||||
unsigned long long m;
|
||||
unsigned short se;
|
||||
} p;
|
||||
} b;
|
||||
|
||||
if (k == 0)
|
||||
return sum;
|
||||
if (k >= 16384)
|
||||
return sum * 2.0L * 0x1p16383L; /* overflow path */
|
||||
if (k <= -16382)
|
||||
{
|
||||
/* result is subnormal: scale up by 2^(k+16382), then by 2^-16382 */
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k + 16382);
|
||||
sum *= b.f;
|
||||
return sum * 0x1p-16382L;
|
||||
}
|
||||
b.p.m = 0x8000000000000000ULL;
|
||||
b.p.se = (unsigned short)(16383 + k);
|
||||
return sum * b.f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/*
|
||||
* Absolute value, all three precisions (C23 7.12.7.2). GCC folds
|
||||
* __builtin_fabs/__builtin_fabsf/__builtin_fabsl into a single
|
||||
* sign-clearing SSE/x87 instruction at every optimization level (verified
|
||||
* at -O0 and -O2), so the call never recurses and no errno path exists.
|
||||
* The sign-bit clear is exact: fabs(-0.0) is +0.0, fabs(±Inf) is +Inf,
|
||||
* and a NaN keeps its payload.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Absolute value of x as a float.
|
||||
*/
|
||||
float
|
||||
fabsf(float x)
|
||||
{
|
||||
return __builtin_fabsf(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute value of x as a double.
|
||||
*/
|
||||
double
|
||||
fabs(double x)
|
||||
{
|
||||
return __builtin_fabs(x);
|
||||
}
|
||||
|
||||
/*
|
||||
* Absolute value of x as a long double.
|
||||
*/
|
||||
long double
|
||||
fabsl(long double x)
|
||||
{
|
||||
return __builtin_fabsl(x);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user