/* * vlibc — public header skeleton test (todo 5). * * Exercises the full public header inventory in one translation unit: * * 1. compile-time: every new header plus the scaffold headers is included * TWICE, so the include guards are proven to tolerate re-inclusion in * any order; static_asserts pin the type widths and the standard macro * values; * 2. runtime: a happy-path main() checks the type sizes, the C23 keywords * the headers provide (nullptr, alignas/alignof, iso646 spellings), * stdatomic, stdbit, stdckdint and stdarg behavior end to end; * 3. failure mode (-f): assert(0) must write the diagnostic to fd 2 and * terminate with SIGILL — __vlibc_assert_fail() ends in * __builtin_trap(), so the harness expects exit status 132 (128 + 4). * When built with -DNDEBUG the -f run prints that assert was swallowed * and exits 0 instead. * * All output goes through the raw SYS_write layer, like tests/syscall_test.c: * no host is included, so the TU exercises only vlibc's own * headers through -Iinclude and stays fully self-contained. * * Not part of the library proper; compiled manually for this todo (the * tests/ + make check wiring is owned by a later todo). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Second round: the guards must make every re-inclusion a no-op. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../src/internal/syscall.h" /* ---- Compile-time gates (features.h wiring) ---- */ #if !VLIBC_HAS_POSIX #error "VLIBC_HAS_POSIX must be 1 at level 1" #endif #if !defined(VLIBC_HAS_HEADER_SYS_TYPES_H) || !defined(VLIBC_HAS_HEADER_UCHAR_H) || \ !defined(VLIBC_HAS_HEADER_CPIO_H) || !defined(VLIBC_HAS_HEADER_TAR_H) || \ !defined(VLIBC_HAS_HEADER_THREADS_H) #error "level-1 header gates missing from features.h" #endif /* ---- Compile-time checks: stdint.h / limits.h / float.h ---- */ static_assert(sizeof(int8_t) == 1 && sizeof(uint8_t) == 1, "8-bit exact types"); static_assert(sizeof(int16_t) == 2 && sizeof(uint16_t) == 2, "16-bit exact types"); static_assert(sizeof(int32_t) == 4 && sizeof(uint32_t) == 4, "32-bit exact types"); static_assert(sizeof(int64_t) == 8 && sizeof(uint64_t) == 8, "64-bit exact types"); static_assert(sizeof(intptr_t) == sizeof(void *) && sizeof(uintptr_t) == sizeof(void *), "pointer-width types"); static_assert(sizeof(intmax_t) == 8 && sizeof(uintmax_t) == 8, "max-width types"); static_assert(INT64_WIDTH == 64 && UINT64_WIDTH == 64, "exact widths"); static_assert(SIZE_WIDTH == 64 && PTRDIFF_WIDTH == 64, "pointer-class widths"); static_assert(WCHAR_WIDTH == 32, "wchar_t is 32-bit int on x86_64"); static_assert(INT32_MAX == 2147483647 && INT32_MIN == (-2147483647 - 1), "int32 limits"); static_assert(UINT32_MAX == 4294967295U, "uint32 limit"); static_assert(INT64_MAX == INT64_C(0x7fffffffffffffff), "INT64_C produces long"); static_assert(UINT64_MAX == UINT64_C(0xffffffffffffffff), "UINT64_C produces unsigned long"); static_assert(CHAR_BIT == 8 && SCHAR_MIN == -128 && UCHAR_MAX == 255, "char limits"); static_assert(SHRT_MAX == 32767 && USHRT_MAX == 65535, "short limits"); static_assert(INT_MAX == 2147483647 && UINT_MAX == 4294967295U, "int limits"); static_assert(LONG_MAX == 0x7fffffffffffffffL && ULONG_MAX == 0xffffffffffffffffUL, "long limits (LP64)"); static_assert(LLONG_MAX == 0x7fffffffffffffffLL && ULLONG_MAX == 0xffffffffffffffffULL, "long long limits"); static_assert(FLT_RADIX == 2, "binary floating point"); static_assert(FLT_MANT_DIG == 24 && DBL_MANT_DIG == 53, "IEC 60559 mantissa widths"); static_assert(FLT_ROUNDS == 1, "default rounding mode"); static_assert(FLT_IS_IEC_60559 == 1 && DBL_IS_IEC_60559 == 1, "IEC 60559 types"); /* ---- Compile-time checks: keyword / feature-test plumbing ---- */ #ifndef __bool_true_false_are_defined #error "stdbool.h must define __bool_true_false_are_defined" #endif #ifndef __alignas_is_defined #error "stdalign.h must define __alignas_is_defined" #endif #ifndef __alignof_is_defined #error "stdalign.h must define __alignof_is_defined" #endif _Static_assert(sizeof(int) == 4, "_Static_assert stays usable alongside static_assert"); /* ---- Runtime harness ---- */ static int failures; /* Write a NUL-terminated string to fd via the raw syscall layer. */ static void say(int fd, const char *s) { size_t n = 0; while (s[n] != '\0') { n++; } (void)__syscall3(SYS_write, fd, (long)s, (long)n); } static void check(int cond, const char *what) { if (!cond) { say(2, "FAIL: "); say(2, what); say(2, "\n"); failures++; } } /* Sum n int arguments via . */ static int sum(int n, ...) { va_list ap; int total = 0; int i; va_start(ap, n); for (i = 0; i < n; i++) { total += va_arg(ap, int); } va_end(ap); return total; } int main(int argc, char **argv) { if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'f') { #ifdef NDEBUG say(1, "NDEBUG: assert(0) swallowed\n"); return 0; #else say(1, "about to assert(0): expect SIGILL\n"); assert(0); return 1; /* unreachable: __vlibc_assert_fail traps */ #endif } /* stddef.h: the C23 pieces. */ { nullptr_t np = nullptr; struct of_test { char c; int i; }; check(sizeof(size_t) == 8 && sizeof(ptrdiff_t) == 8, "size_t/ptrdiff_t widths"); check(sizeof(wchar_t) == 4, "wchar_t width"); check(sizeof(nullptr_t) == sizeof(void *), "nullptr_t size"); check(np == nullptr, "nullptr constant round-trip"); check(offsetof(struct of_test, i) == 4, "offsetof"); check(NULL == 0, "NULL"); } /* stdalign.h / C23 keywords. */ { alignas(16) int aligned_obj; check(alignof(double) >= 8, "alignof"); check(((uintptr_t)&aligned_obj & 15) == 0, "alignas(16) honored"); } /* iso646.h: alternative spellings are macros in C23. */ { int alt = (1 and 1) or 0; check(alt == 1, "iso646 and/or"); check(not(alt == 0), "iso646 not"); } /* stdbit.h: type-generic dispatch and per-width spellings. */ check(stdc_leading_zeros(1u) == 31, "stdc_leading_zeros"); check(stdc_leading_zeros_uc(0x80u) == 0, "stdc_leading_zeros_uc"); check(stdc_leading_ones(0xffffffffu) == 32, "stdc_leading_ones full"); check(stdc_trailing_zeros(0x100u) == 8, "stdc_trailing_zeros"); check(stdc_trailing_ones(7u) == 3, "stdc_trailing_ones"); check(stdc_count_ones(0xffu) == 8, "stdc_count_ones"); check(stdc_count_zeros(0xffu) == 24, "stdc_count_zeros"); check(stdc_has_single_bit(0x40u) == 1, "stdc_has_single_bit true"); check(stdc_has_single_bit(0x41u) == 0, "stdc_has_single_bit false"); check(stdc_bit_width(0u) == 0, "stdc_bit_width(0)"); check(stdc_bit_width(0x40u) == 7, "stdc_bit_width"); check(stdc_bit_floor(0x33u) == 0x20u, "stdc_bit_floor"); check(stdc_bit_ceil(0x33u) == 0x40u, "stdc_bit_ceil"); check(stdc_first_leading_one((unsigned char)0x80) == 0, "stdc_first_leading_one"); check(stdc_first_leading_zero(0xfffffffeu) == 31, "stdc_first_leading_zero"); check(stdc_first_trailing_one(0x80u) == 7, "stdc_first_trailing_one"); check(stdc_first_trailing_zero(1u) == 1, "stdc_first_trailing_zero"); /* stdckdint.h: overflow detection and result preservation. */ { unsigned int ures = 0; int sres = 0; check(ckd_add(&ures, 0xffffffffu, 1u) == 1, "ckd_add detects overflow"); check(ures == 0, "ckd_add leaves result unmodified on overflow"); check(ckd_add(&ures, 1u, 2u) == 0 && ures == 3u, "ckd_add computes"); check(ckd_mul(&sres, 100000, 100000) == 1, "ckd_mul detects overflow"); check(ckd_sub(&ures, 5u, 7u) == 1, "ckd_sub detects underflow"); } /* stdatomic.h: the macro operations over GCC builtins. */ { atomic_int counter = 7; atomic_flag flag = ATOMIC_FLAG_INIT; int expected; check(atomic_load(&counter) == 7, "atomic_load"); atomic_store(&counter, 5); check(atomic_load(&counter) == 5, "atomic_store"); check(atomic_fetch_add(&counter, 2) == 5 && atomic_load(&counter) == 7, "atomic_fetch_add"); check(atomic_exchange(&counter, 1) == 7, "atomic_exchange"); expected = 1; check(atomic_compare_exchange_strong(&counter, &expected, 9) == 1 && atomic_load(&counter) == 9, "compare_exchange success"); expected = 3; check(atomic_compare_exchange_strong(&counter, &expected, 0) == 0 && expected == 9, "compare_exchange failure updates expected"); check(atomic_flag_test_and_set(&flag) == 0, "atomic_flag initially clear"); check(atomic_flag_test_and_set(&flag) == 1, "atomic_flag now set"); atomic_flag_clear(&flag); check(atomic_flag_test_and_set(&flag) == 0, "atomic_flag cleared"); check(atomic_is_lock_free(&counter) != 0, "atomic_int lock-free"); atomic_thread_fence(memory_order_seq_cst); atomic_signal_fence(memory_order_relaxed); } /* stdarg.h: variadic round-trip. */ check(sum(3, 1, 2, 3) == 6, "stdarg sum"); check(sum(0) == 0, "stdarg empty call"); /* assert.h happy path: a true assertion is a no-op. */ assert(1); assert(sizeof(int) == 4); check(1, "assert(true) no-op"); /* uchar.h: C23 keyword types and the declared-only conversions. */ check(sizeof(char16_t) == 2, "char16_t width"); check(sizeof(char32_t) == 4, "char32_t width"); { mbstate_t mbs; mbs.state[0] = 0; mbs.state[1] = 0; check(mbs.state[0] == 0 && mbs.state[1] == 0, "mbstate_t usable"); } /* sys/types.h: the POSIX scalar set (x86_64 LP64). */ static_assert(sizeof(ssize_t) == 8 && sizeof(off_t) == 8, "signed 64-bit types"); static_assert(sizeof(time_t) == 8 && sizeof(clock_t) == 8, "time types"); static_assert(sizeof(pid_t) == 4 && sizeof(uid_t) == 4 && sizeof(gid_t) == 4, "id types"); static_assert(sizeof(mode_t) == 4 && sizeof(id_t) == 4 && sizeof(key_t) == 4, "mode/id/key types"); static_assert(sizeof(dev_t) == 8 && sizeof(ino_t) == 8 && sizeof(nlink_t) == 8, "file identity types"); static_assert(sizeof(blkcnt_t) == 8 && sizeof(blksize_t) == 8, "block types"); static_assert(sizeof(suseconds_t) == 8 && sizeof(useconds_t) == 4, "usec types"); /* cpio.h / tar.h: the archive constants. */ check(C_IRUSR == 0400 && C_IRGRP == 0040 && C_IROTH == 0004, "cpio permission bits"); check(C_ISREG == 0100000 && C_ISDIR == 0040000 && C_ISLNK == 0120000, "cpio type bits"); check(TMAGIC[0] == 'u' && TMAGIC[1] == 's' && TMAGLEN == 6, "tar magic"); check(TVERSION[0] == '0' && TVERSLEN == 2, "tar version"); check(REGTYPE == '0' && AREGTYPE == '\0' && LNKTYPE == '1' && DIRTYPE == '5', "tar typeflags"); check(TSUID == 04000 && TUREAD == 00400 && TGREAD == 00040 && TOREAD == 00004, "tar mode bits"); /* threads.h stub: the types exist; the functions stay unimplemented. */ { once_flag of = ONCE_FLAG_INIT; thrd_t t = 0; check(of.opaque == 0 && t == 0, "threads.h stub types"); check(TSS_DTOR_ITERATIONS == 4, "TSS_DTOR_ITERATIONS"); } if (failures > 0) { say(2, "FAILED\n"); } return failures == 0 ? 0 : 1; }