diff --git a/.gitignore b/.gitignore index b97e859..13a74bd 100644 --- a/.gitignore +++ b/.gitignore @@ -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-*/ + + diff --git a/Makefile.am b/Makefile.am index aad6130..67f2252 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,23 +2,110 @@ 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: +# +# W1 (wired now): src/internal, src/start, src/errno, +# src/setjmp/x86_64, src/string, crt/ (crt1.o) +# todo 7 src/malloc 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). +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/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 + +# Profile-gated sources, EXISTING gates preserved (strlcpy/strlcat stay L2 +# BSD, strcasestr stays L3 GNU — NOT promoted). +VLIBC_LEVEL2_SRCS = src/string/strlcpy.c src/string/strlcat.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/errno.h \ + include/setjmp.h \ + include/assert.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/stdnoreturn.h \ + include/tar.h \ + include/tgmath.h \ + include/threads.h \ + include/uchar.h \ + include/sys/types.h + # Install location depends on the install method (see configure.ac). if INSTALL_OVERWRITE vlibc_includedir = $(includedir) @@ -38,6 +125,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 +148,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 +156,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 +168,122 @@ 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 +# `_SOURCES = tests/test_x.c`, +# `_CFLAGS = $(VLIBC_TEST_CFLAGS)` (renames the object to +# `-.o` automatically), +# `_LINK = $(VLIBC_TEST_LINK)` and +# `_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 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 +# "$(_LINK) $(_OBJECTS) $(_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_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_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 four binaries. +TESTS = test_syscall test_strerror test_setjmp test_headers \ + 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/libc.h \ + src/internal/syscall.h \ + src/internal/types.h \ + src/start/start.h \ + src/start/tcb.h diff --git a/benchmarks/Makefile.am b/benchmarks/Makefile.am index 58d1827..68fae2a 100644 --- a/benchmarks/Makefile.am +++ b/benchmarks/Makefile.am @@ -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 diff --git a/configure b/configure index 754a2d2..afad360 100755 --- a/configure +++ b/configure @@ -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 CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory + 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 diff --git a/configure.ac b/configure.ac index 9bbf7dc..65853d3 100644 --- a/configure.ac +++ b/configure.ac @@ -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; diff --git a/include/vlibc/internal/test.h b/include/vlibc/internal/test.h new file mode 100644 index 0000000..107d571 --- /dev/null +++ b/include/vlibc/internal/test.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 : 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 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 + +#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 */ diff --git a/tests/conformance/symbols.list b/tests/conformance/symbols.list new file mode 100644 index 0000000..ca03247 --- /dev/null +++ b/tests/conformance/symbols.list @@ -0,0 +1,1117 @@ +# vlibc — authoritative L1+L2 symbol manifest (todo 6). +# +# The working conformance target for the POSIX.1-2008 surface (L1, onlyposix) +# plus the muslmimic surface (L2). One symbol per line; lines are LC_ALL=C +# sorted for deterministic diffs. A bare name is L1 (POSIX.1-2008 base / +# ISO C); a trailing " L2" marks a symbol gated at VLIBC_LEVEL >= 2 (XSI, +# BSD, or obsolescent-[OB] compat per the plan's per-todo gate sentences). +# L3-only symbols (strcasestr, open_wmemstream, versionsort, wcpcpy, +# wcpncpy, getopt_long, the GNU *_r database forms, ...) and internal ABI +# helpers (__errno_location, __libc_start_main, __syscall*, vlibc_version) +# are deliberately NOT listed: this manifest covers the POSIX function +# surface, not the implementation ABI. +# +# Enumerated from the What-to-do lists of todos 1-69 in +# .omo/plans/full-posix.md, with every prose shorthand expanded literally +# ("f/l variants", "(all float/long double)", "c...f/c...l", "*_r" forms, +# get*/get*_r pairs, v* printf/scanf variants, isw*_l/tow*_l sets) — per +# todo 6's instruction this is authored by reading the lists, not by grep. +# Task 69 verifies this manifest against the delivered symbols (and may +# correct it in place for classification errors); final-verifier F4 +# re-derives the L1+L2 set independently from IEEE 1003.1-2008 and diffs +# against it. Functions only: variables (environ, optarg, optind, opterr, +# optopt, tzname, daylight, timezone, signgam) and constants are not listed. +_Exit +_exit +_longjmp +_setjmp +abort +abs +accept +access +acos +acosf +acosh +acoshf +acoshl +acosl +aio_cancel +aio_error +aio_fsync +aio_read +aio_return +aio_suspend +aio_write +alarm +aligned_alloc +alphasort L2 +asctime +asctime_r L2 +asin +asinf +asinh +asinhf +asinhl +asinl +asprintf L2 +at_quick_exit +atan +atan2 +atan2f +atan2l +atanf +atanh +atanhf +atanhl +atanl +atexit +atof +atoi +atol +atoll +basename L2 +bcmp L2 +bcopy L2 +bind +bsearch +btowc +bzero L2 +c16rtomb +c32rtomb +cabs +cabsf +cabsl +cacos +cacosf +cacosh +cacoshf +cacoshl +cacosl +calloc +carg +cargf +cargl +casin +casinf +casinh +casinhf +casinhl +casinl +catan +catanf +catanh +catanhf +catanhl +catanl +catclose L2 +catgets L2 +catopen L2 +cbrt +cbrtf +cbrtl +ccos +ccosf +ccosh +ccoshf +ccoshl +ccosl +ceil +ceilf +ceill +cexp +cexpf +cexpl +cfgetispeed +cfgetospeed +cfmakeraw L2 +cfsetispeed +cfsetospeed +cfsetspeed L2 +chdir +chmod +chown +chroot L2 +cimag +clearenv L2 +clearerr +clock +clock_getcpuclockid +clock_getres +clock_gettime +clock_nanosleep +clock_settime +clog +clogf +clogl +close +closedir +closelog L2 +cnd_signal +cnd_wait +confstr +conj +connect +copysign +copysignf +copysignl +cos +cosf +cosh +coshf +coshl +cosl +cpolar +cpolarf +cpolarl +cpow +cpowf +cpowl +cproj +cprojf +cprojl +creal +creat +crypt L2 +csin +csinf +csinh +csinhf +csinhl +csinl +csqrt +csqrtf +csqrtl +ctan +ctanf +ctanh +ctanhf +ctanhl +ctanl +ctermid L2 +ctime +ctime_r L2 +daemon L2 +dbm_clearerr L2 +dbm_close L2 +dbm_delete L2 +dbm_error L2 +dbm_fetch L2 +dbm_firstkey L2 +dbm_nextkey L2 +dbm_open L2 +dbm_store L2 +difftime +dirfd +dirname L2 +div +dlclose +dlerror +dlopen +dlsym +dprintf +drand48 L2 +dup +dup2 +dup3 L2 +duplocale L2 +encrypt L2 +endgrent +endprotoent +endpwent +endservent +endutxent L2 +erand48 L2 +erf +erfc +err L2 +errx L2 +execl +execle +execlp +execv +execve +execvp +exit +exp +exp2 +exp2f +exp2l +expf +expl +explicit_bzero L2 +expm1 +expm1f +expm1l +fabs +fabsf +fabsl +faccessat +fchdir +fchmod +fchmodat +fchown +fchownat +fclose +fcntl +fdatasync +fdim +fdimf +fdiml +fdopen +fdopendir +feof +ferror +fexecve +fflush +ffs L2 +ffsl L2 +ffsll L2 +fgetc +fgetgrent L2 +fgetpos +fgetpwent L2 +fgets +fgetwc +fgetws +fileno +flockfile +floor +floorf +floorl +fma +fmaf +fmax +fmaxf +fmaxl +fmemopen +fmin +fminf +fminl +fmod +fmodf +fmodl +fmtmsg L2 +fnmatch +fopen +fopen64 L2 +fopencookie L2 +fork +fpathconf +fprintf +fputc +fputs +fputwc +fputws +fread +free +freeaddrinfo +freelocale L2 +freopen +frexp +frexpf +frexpl +fscanf +fseek +fseeko +fsetpos +fstat +fstatat +fstatvfs L2 +fsync +ftell +ftello +ftime L2 +ftok L2 +ftruncate +ftrylockfile +ftw L2 +funlockfile +futimens +futimes L2 +fwide +fwprintf +fwrite +fwscanf +gai_strerror +getaddrinfo +getc +getc_unlocked +getchar +getchar_unlocked +getcontext L2 +getcwd +getdelim +getegid +getenv +geteuid +getgid +getgrent +getgrgid +getgrgid_r +getgrnam +getgrnam_r +getgroups +gethostid L2 +gethostname L2 +getitimer L2 +getline +getlogin +getlogin_r +getnameinfo +getopt +getpeername +getpgid L2 +getpgrp +getpid +getppid +getpriority L2 +getprotobyname +getprotobynumber +getprotoent +getpwent +getpwnam +getpwnam_r +getpwuid +getpwuid_r +getrlimit L2 +getrlimit64 L2 +getrusage L2 +getservbyname +getservbyport +getservent +getsid L2 +getsockname +getsockopt +getsubopt L2 +gettimeofday L2 +getuid +getutxent L2 +getutxid L2 +getutxline L2 +getwc +getwchar +getwd L2 +glob L2 +globfree L2 +gmtime +gmtime_r +grantpt L2 +hcreate L2 +hdestroy L2 +hsearch L2 +htonl +htons +hypot +hypotf +hypotl +iconv L2 +iconv_close L2 +iconv_open L2 +if_freenameindex L2 +if_indextoname L2 +if_nameindex L2 +if_nametoindex L2 +ilogb +ilogbf +ilogbl +imaxabs +imaxdiv +index L2 +inet_addr L2 +inet_aton L2 +inet_ntoa L2 +inet_ntop +inet_pton +initstate L2 +insque L2 +isalnum +isalpha +isascii L2 +isatty +isblank +iscntrl +isdigit +isgraph +islower +isprint +ispunct +isspace +isupper +iswalnum +iswalnum_l L2 +iswalpha +iswalpha_l L2 +iswblank +iswblank_l L2 +iswcntrl +iswcntrl_l L2 +iswctype +iswdigit +iswdigit_l L2 +iswgraph +iswgraph_l L2 +iswlower +iswlower_l L2 +iswprint +iswprint_l L2 +iswpunct +iswpunct_l L2 +iswspace +iswspace_l L2 +iswupper +iswupper_l L2 +iswxdigit +iswxdigit_l L2 +isxdigit +jrand48 L2 +kill +killpg +labs +lchown +lcong48 L2 +ldexp +ldexpf +ldexpl +ldiv +lfind L2 +lgamma +lgamma_r L2 +lgammaf_r L2 +lgammal_r L2 +link +linkat +lio_listio +listen +llabs +lldiv +llrint +llrintf +llrintl +llround +llroundf +llroundl +localeconv +localtime +localtime_r +lockf L2 +log +log10 +log10f +log10l +log1p +log1pf +log1pl +log2 +log2f +log2l +logb +logbf +logbl +logf +logl +longjmp +lrand48 L2 +lrint +lrintf +lrintl +lround +lroundf +lroundl +lsearch L2 +lseek +lseek64 L2 +lstat +lutimes L2 +madvise L2 +makecontext L2 +malloc +malloc_usable_size L2 +mblen +mbrlen +mbrtoc16 +mbrtoc32 +mbrtowc +mbsinit +mbsrtowcs +mbstowcs +mbtowc +memccpy L2 +memchr +memcmp +memcpy +memmem L2 +memmove +memset +mkdir +mkdirat +mkdtemp +mkfifo +mkfifoat +mknod L2 +mknodat L2 +mkstemp +mktemp L2 +mktime +mlock +mlockall +mmap +modf +modff +modfl +mprotect +mq_close +mq_getattr +mq_notify +mq_open +mq_receive +mq_send +mq_setattr +mq_timedreceive +mq_timedsend +mq_unlink +mrand48 L2 +msgctl L2 +msgget L2 +msgrcv L2 +msgsnd L2 +msync +mtx_init +mtx_lock +mtx_unlock +munlock +munlockall +munmap +nan +nanf +nanosleep +nearbyint +nearbyintf +nearbyintl +newlocale L2 +nextafter +nexttoward +nftw L2 +nice L2 +nl_langinfo L2 +nrand48 L2 +ntohl +ntohs +open +open_memstream +openat +opendir +openlog L2 +pathconf +pause +pclose +perror +pipe +pipe2 L2 +poll +popen +posix_fadvise +posix_fallocate +posix_madvise +posix_memalign +posix_openpt L2 +posix_spawn +posix_spawn_file_actions_addclose +posix_spawn_file_actions_adddup2 +posix_spawn_file_actions_addopen +posix_spawn_file_actions_destroy +posix_spawn_file_actions_init +posix_spawnattr_destroy +posix_spawnattr_getflags +posix_spawnattr_getpgroup +posix_spawnattr_getschedparam +posix_spawnattr_getschedpolicy +posix_spawnattr_getsigdefault +posix_spawnattr_getsigmask +posix_spawnattr_init +posix_spawnattr_setflags +posix_spawnattr_setpgroup +posix_spawnattr_setschedparam +posix_spawnattr_setschedpolicy +posix_spawnattr_setsigdefault +posix_spawnattr_setsigmask +posix_spawnp +pow +powf +powl +ppoll L2 +pread +printf +pselect +psiginfo L2 +psignal L2 +pthread_atfork +pthread_attr_destroy +pthread_attr_getdetachstate +pthread_attr_getguardsize +pthread_attr_getinheritsched +pthread_attr_getschedparam +pthread_attr_getschedpolicy +pthread_attr_getscope +pthread_attr_getstack +pthread_attr_getstackaddr L2 +pthread_attr_getstacksize +pthread_attr_init +pthread_attr_setdetachstate +pthread_attr_setguardsize +pthread_attr_setinheritsched +pthread_attr_setschedparam +pthread_attr_setschedpolicy +pthread_attr_setscope +pthread_attr_setstack +pthread_attr_setstackaddr L2 +pthread_attr_setstacksize +pthread_barrier_destroy +pthread_barrier_init +pthread_barrier_wait +pthread_barrierattr_destroy +pthread_barrierattr_getpshared +pthread_barrierattr_init +pthread_barrierattr_setpshared +pthread_cancel +pthread_cond_broadcast +pthread_cond_destroy +pthread_cond_init +pthread_cond_signal +pthread_cond_timedwait +pthread_cond_wait +pthread_condattr_destroy +pthread_condattr_getclock +pthread_condattr_getpshared +pthread_condattr_init +pthread_condattr_setclock +pthread_condattr_setpshared +pthread_create +pthread_detach +pthread_equal +pthread_exit +pthread_getcpuclockid +pthread_getschedparam +pthread_getspecific +pthread_join +pthread_key_create +pthread_key_delete +pthread_kill +pthread_mutex_consistent L2 +pthread_mutex_destroy +pthread_mutex_getprioceiling L2 +pthread_mutex_init +pthread_mutex_lock +pthread_mutex_setprioceiling L2 +pthread_mutex_timedlock +pthread_mutex_trylock +pthread_mutex_unlock +pthread_mutexattr_destroy +pthread_mutexattr_getprioceiling L2 +pthread_mutexattr_getprotocol L2 +pthread_mutexattr_getpshared +pthread_mutexattr_getrobust L2 +pthread_mutexattr_gettype +pthread_mutexattr_init +pthread_mutexattr_setprioceiling L2 +pthread_mutexattr_setprotocol L2 +pthread_mutexattr_setpshared +pthread_mutexattr_setrobust L2 +pthread_mutexattr_settype +pthread_once +pthread_rwlock_destroy +pthread_rwlock_init +pthread_rwlock_rdlock +pthread_rwlock_timedrdlock +pthread_rwlock_timedwrlock +pthread_rwlock_tryrdlock +pthread_rwlock_trywrlock +pthread_rwlock_unlock +pthread_rwlock_wrlock +pthread_rwlockattr_destroy +pthread_rwlockattr_getpshared +pthread_rwlockattr_init +pthread_rwlockattr_setpshared +pthread_self +pthread_setcancelstate +pthread_setcanceltype +pthread_setschedparam +pthread_setschedprio +pthread_setspecific +pthread_sigmask +pthread_spin_destroy +pthread_spin_init +pthread_spin_lock +pthread_spin_trylock +pthread_spin_unlock +pthread_testcancel +ptsname L2 +putc +putc_unlocked +putchar +putchar_unlocked +putenv L2 +putgrent L2 +putpwent L2 +puts +pututxline L2 +putwc +putwchar +pwrite +qsort +quick_exit +raise +rand +rand_r L2 +random L2 +read +readdir +readdir_r L2 +readlink +readlinkat +readv +realloc +realpath L2 +recv +recvfrom +recvmsg +regcomp L2 +regerror L2 +regexec L2 +regfree L2 +remainder +remainderf +remainderl +remove +remque L2 +remquo +remquof +remquol +rename +renameat +rewind +rewinddir +rindex L2 +rint +rintf +rintl +rmdir +round +roundf +roundl +scalbln +scalblnf +scalblnl +scalbn +scalbnf +scalbnl +scandir L2 +scanf +sched_get_priority_max +sched_get_priority_min +sched_getparam +sched_getscheduler +sched_rr_get_interval +sched_setparam +sched_setscheduler +sched_yield +seed48 L2 +seekdir +select +sem_close +sem_destroy +sem_getvalue +sem_init +sem_open +sem_post +sem_timedwait +sem_trywait +sem_unlink +sem_wait +semctl L2 +semget L2 +semop L2 +send +sendmsg +sendto +setbuf +setbuffer L2 +setcontext L2 +setegid +setenv +seteuid +setgid +setgrent +setgroups L2 +sethostname L2 +setitimer L2 +setjmp +setkey L2 +setlinebuf L2 +setlocale +setlogmask L2 +setpgid +setpgrp L2 +setpriority L2 +setprotoent +setpwent +setrlimit L2 +setrlimit64 L2 +setservent +setsid +setsockopt +setstate L2 +settimeofday L2 +setuid +setutxent L2 +setvbuf +shm_open +shm_unlink +shmat L2 +shmctl L2 +shmdt L2 +shmget L2 +shutdown +sigaction +sigaddset +sigaltstack L2 +sigdelset +sigemptyset +sigfillset +siginterrupt L2 +sigismember +siglongjmp +signal +signbit +sigpending +sigprocmask +sigqueue +sigsetjmp +sigsuspend +sigtimedwait +sigwait +sigwaitinfo +sin +sinf +sinh +sinhf +sinhl +sinl +sleep +snprintf +sockatmark +socket +socketpair +sprintf +sqrt +sqrtf +sqrtl +srand +srand48 L2 +srandom L2 +sscanf +stat +statvfs L2 +stpcpy L2 +stpncpy L2 +strcasecmp L2 +strcat +strchr +strchrnul L2 +strcmp +strcoll +strcoll_l L2 +strcpy +strcspn +strdup +strerror +strerror_r L2 +strfmon L2 +strftime +strftime_l L2 +strlcat L2 +strlcpy L2 +strlen +strncasecmp L2 +strncat +strncmp +strncpy +strndup +strnlen +strpbrk +strptime L2 +strrchr +strsep L2 +strsignal +strspn +strstr +strtod +strtof +strtoimax +strtok +strtok_r +strtol +strtold +strtoll +strtoul +strtoull +strtoumax +strxfrm +strxfrm_l L2 +swab L2 +swapcontext L2 +swprintf +swscanf +symlink +symlinkat +sync +sysconf +sysinfo L2 +syslog L2 +system +tan +tanf +tanh +tanhf +tanhl +tanl +tcdrain +tcflow +tcflush +tcgetattr +tcgetpgrp L2 +tcgetsid L2 +tcsendbreak +tcsetattr +tcsetpgrp L2 +tdelete L2 +telldir +tfind L2 +tgamma +thrd_create +thrd_detach +thrd_exit +thrd_join +thrd_yield +time +timegm L2 +timer_create +timer_delete +timer_getoverrun +timer_gettime +timer_settime +times L2 +timespec_get +timingsafe_memcmp L2 +tmpfile +tmpnam L2 +toascii L2 +tolower +toupper +towctrans +towlower +towlower_l L2 +towupper +towupper_l L2 +trunc +truncate L2 +truncf +truncl +tsearch L2 +tss_create +tss_get +tss_set +ttyname +ttyname_r +twalk L2 +tzset +ualarm L2 +ulimit L2 +umask +uname L2 +ungetc +ungetwc +unlink +unlinkat +unlockpt L2 +unsetenv +uselocale L2 +usleep L2 +utime L2 +utimensat +utimes L2 +vasprintf L2 +vdprintf +verr L2 +verrx L2 +vfork L2 +vfprintf +vfscanf +vfwprintf +vfwscanf +vprintf +vscanf +vsnprintf +vsprintf +vsscanf +vswprintf +vswscanf +vsyslog L2 +vwarn L2 +vwarnx L2 +vwprintf +vwscanf +wait +wait3 L2 +wait4 L2 +waitid +waitpid +warn L2 +warnx L2 +wcrtomb +wcscat +wcschr +wcscmp +wcscoll +wcscpy +wcscspn +wcsdup +wcsftime +wcslen +wcsncat +wcsncmp +wcsncpy +wcsnlen L2 +wcspbrk +wcsrchr +wcsrtombs +wcsspn +wcsstr +wcstod +wcstof +wcstoimax +wcstok +wcstol +wcstold +wcstoll +wcstombs +wcstoul +wcstoull +wcstoumax +wcswidth L2 +wcsxfrm +wctob +wctomb +wctrans +wctype +wcwidth L2 +wmemchr +wmemcmp +wmemcpy +wmemmove +wmemset +wordexp L2 +wordfree L2 +wprintf +write +writev +wscanf diff --git a/tests/startup_modes.sh b/tests/startup_modes.sh new file mode 100755 index 0000000..1f3b1cf --- /dev/null +++ b/tests/startup_modes.sh @@ -0,0 +1,95 @@ +#!/bin/sh +# vlibc — make check mode matrix (todo 6). +# +# Runs every scenario that a single "run the binary once" TESTS entry cannot +# cover: the -f failure modes of the four syscall/errno/setjmp/headers tests +# and the full test_startup mode matrix. Every scenario pins its exact exit +# status and (where load-bearing) its exact stdout; see tests/test_startup.c +# and the individual tests for the mode contracts. +# +# The binaries are the static vlibc executables built by make check next to +# the top-level Makefile. make check executes this script with the build +# directory root as the working directory (in-tree and VPATH builds alike), +# so the binaries resolve as plain ./ paths relative to the CWD, NOT +# relative to this script's location. + +fail=0 + +# run_rc [args...] +# Runs the binary with the given args and checks the exit status only (the +# -f modes print diagnostics, whose text is not part of the contract). +run_rc() +{ + bin=$1 + want_rc=$2 + desc=$3 + shift 3 + "$bin" "$@" >/dev/null 2>&1 + rc=$? + if [ "$rc" -ne "$want_rc" ]; then + echo "FAIL: $desc: rc=$rc want=$want_rc" + fail=1 + else + echo "ok: $desc" + fi +} + +# run [args...] +# Like run_rc but also compares the exact stdout (stderr discarded). An +# empty want_stdout matches an empty stdout. +run() +{ + bin=$1 + want_rc=$2 + want_out=$3 + desc=$4 + shift 4 + out=$("$bin" "$@" 2>/dev/null) + rc=$? + if [ "$rc" -ne "$want_rc" ]; then + echo "FAIL: $desc: rc=$rc want=$want_rc" + fail=1 + elif [ "$out" != "$want_out" ]; then + echo "FAIL: $desc: stdout [$out] want [$want_out]" + fail=1 + else + echo "ok: $desc" + fi +} + +bindir=. + +# -f failure scenarios of the four arg-mode tests (exit status is the +# contract; the diagnostics on stdout are not pinned). +run_rc "./test_syscall" 0 "syscall -f: ENOENT via fake TCB slot" -f +run_rc "./test_strerror" 0 "strerror -f: unknown errno formats" -f +run_rc "./test_setjmp" 0 "setjmp -f: longjmp(env,0) coerced to 1" -f +run_rc "./test_headers" 132 "headers -f: assert(0) traps (SIGILL)" -f + +# test_startup mode matrix (each mode pins its own exit status; the default +# mode exits 42 on success, which is why it is not a bare TESTS entry). +run "./test_startup" 42 "C +B +A" "startup default: atexit LIFO CBA, status 42" +run "./test_startup" 5 "Q" "startup -q: quick_exit runs only at_quick_exit" -q +run "./test_startup" 7 "" "startup -z: _exit runs no handlers" -z +run "./test_startup" 0 "" "startup -t: main-thread TLS + errno TCB slot" -t +run "./test_startup" 0 "" "startup -x: handler-list limits" -x + +# -e needs VLIBC_TEST_VAR=hello in environ (checked by the test itself). +out=$(VLIBC_TEST_VAR=hello "./test_startup" -e extraarg 2>/dev/null) +rc=$? +if [ "$rc" -ne 0 ]; then + echo "FAIL: startup -e: argv/env plumbing rc=$rc want=0" + fail=1 +elif [ "$out" != "argc=3 +argv0=./test_startup +argv1=-e +argv2=extraarg" ]; then + echo "FAIL: startup -e: unexpected stdout [$out]" + fail=1 +else + echo "ok: startup -e: argv/env plumbing" +fi + +exit $fail