diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 060b455..8b459f9 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 16 +#define MONTAUK_BUILD_NUMBER 18 diff --git a/programs/GNUmakefile b/programs/GNUmakefile index 7f00baf..9cea8c1 100644 --- a/programs/GNUmakefile +++ b/programs/GNUmakefile @@ -401,8 +401,10 @@ ifneq ($(wildcard $(NATIVE_BIN)/as),) cp lib/libc/liblibc.a $(BINDIR)/sdk/lib/libc.a cp lib/libc/crt1.o lib/libc/crti.o lib/libc/crtn.o $(BINDIR)/sdk/lib/ ar rcs $(BINDIR)/sdk/lib/libm.a - ar rcs $(BINDIR)/sdk/lib/libstdc++.a + cp ../toolchain/local/x86_64-montauk/lib/libstdc++.a $(BINDIR)/sdk/lib/libstdc++.a + cp -r ../toolchain/local/x86_64-montauk/include/c++ $(BINDIR)/sdk/include/c++ ../toolchain/local/bin/x86_64-montauk-gcc -O2 ../toolchain/files/tls-test.c -o $(BINDIR)/sdk/bin/tls-test.elf + ../toolchain/local/bin/x86_64-montauk-g++ -O2 ../toolchain/files/cxx-test.cpp -o $(BINDIR)/sdk/bin/cxx-test.elf else @echo "devkit: toolchain/native/ not built; skipping native dev tools" endif diff --git a/programs/include/libc/dirent.h b/programs/include/libc/dirent.h index 397d0ca..5e90839 100644 --- a/programs/include/libc/dirent.h +++ b/programs/include/libc/dirent.h @@ -9,8 +9,13 @@ extern "C" { #define NAME_MAX 255 #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 struct dirent { unsigned char d_type; diff --git a/programs/include/libc/errno.h b/programs/include/libc/errno.h index 3459869..40fd8b9 100644 --- a/programs/include/libc/errno.h +++ b/programs/include/libc/errno.h @@ -49,8 +49,47 @@ extern int errno; #define ENOTEMPTY 39 #define ELOOP 40 #define EWOULDBLOCK EAGAIN +#define ETXTBSY 26 +#define ENOMSG 42 +#define EIDRM 43 +#define ENOSTR 60 +#define ENODATA 61 +#define ETIME 62 +#define ENOSR 63 +#define ENOLINK 67 +#define EPROTO 71 +#define EMULTIHOP 72 +#define EBADMSG 74 #define EOVERFLOW 75 +#define EILSEQ 84 +#define ENOTSOCK 88 +#define EDESTADDRREQ 89 +#define EMSGSIZE 90 +#define EPROTOTYPE 91 +#define ENOPROTOOPT 92 +#define EPROTONOSUPPORT 93 +#define EOPNOTSUPP 95 /* == ENOTSUP, Linux numbering */ +#define EAFNOSUPPORT 97 +#define EADDRINUSE 98 +#define EADDRNOTAVAIL 99 +#define ENETDOWN 100 +#define ENETUNREACH 101 +#define ENETRESET 102 +#define ECONNABORTED 103 +#define ECONNRESET 104 +#define ENOBUFS 105 +#define EISCONN 106 +#define ENOTCONN 107 #define ETIMEDOUT 110 +#define ECONNREFUSED 111 +#define EHOSTUNREACH 113 +#define EALREADY 114 +#define EINPROGRESS 115 +#define ESTALE 116 +#define EDQUOT 122 +#define ECANCELED 125 +#define EOWNERDEAD 130 +#define ENOTRECOVERABLE 131 #define ENOTSUP 95 #ifdef __cplusplus diff --git a/programs/include/libc/math.h b/programs/include/libc/math.h index efbce2f..6f2b779 100644 --- a/programs/include/libc/math.h +++ b/programs/include/libc/math.h @@ -11,8 +11,47 @@ extern "C" { #define INFINITY __builtin_inff() #define NAN __builtin_nanf("") +/* C99 floating-point classification. */ +#define FP_NAN 0 +#define FP_INFINITE 1 +#define FP_ZERO 2 +#define FP_SUBNORMAL 3 +#define FP_NORMAL 4 + +#define fpclassify(x) \ + __builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, \ + FP_ZERO, x) +#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) + double fabs(double x); double frexp(double x, int *exp); + +/* C89 modf and the C99 float variants assumed by hosted libstdc++ + (--with-newlib crossconfig). The float forms wrap the double + implementations. */ +double modf(double x, double *iptr); +float modff(float x, float *iptr); +double hypot(double x, double y); +float hypotf(float x, float y); +float acosf(float x); +float asinf(float x); +float atanf(float x); +float atan2f(float y, float x); +float coshf(float x); +float expf(float x); +float fmodf(float x, float y); +float frexpf(float x, int *exp); +float ldexpf(float x, int exp); +float logf(float x); +float log10f(float x); +float powf(float x, float y); +float sinhf(float x); +float tanf(float x); +float tanhf(float x); double ldexp(double x, int n); long double ldexpl(long double x, int n); double floor(double x); diff --git a/programs/include/libc/stdio.h b/programs/include/libc/stdio.h index 0a2dac3..24b7489 100644 --- a/programs/include/libc/stdio.h +++ b/programs/include/libc/stdio.h @@ -39,6 +39,15 @@ extern FILE *stderr; int putc(int c, FILE *stream); void rewind(FILE *stream); int getchar(void); +typedef long fpos_t; + +int fgetpos(FILE *stream, fpos_t *pos); +int fsetpos(FILE *stream, const fpos_t *pos); +void setbuf(FILE *stream, char *buf); +int fscanf(FILE *stream, const char *fmt, ...); +int scanf(const char *fmt, ...); +int vfscanf(FILE *stream, const char *fmt, va_list ap); + int fileno(FILE *stream); FILE *fdopen(int fd, const char *mode); diff --git a/programs/include/libc/stdlib.h b/programs/include/libc/stdlib.h index 741bcd8..54d8a30 100644 --- a/programs/include/libc/stdlib.h +++ b/programs/include/libc/stdlib.h @@ -63,6 +63,8 @@ char *mktemp(char *template_); char *realpath(const char *path, char *resolved); size_t mbstowcs(wchar_t *dst, const char *src, size_t n); int mblen(const char *s, size_t n); +int mbtowc(wchar_t *pwc, const char *s, size_t n); +int wctomb(char *s, wchar_t wc); double strtod(const char *nptr, char **endptr); float strtof(const char *nptr, char **endptr); long double strtold(const char *nptr, char **endptr); diff --git a/programs/include/libc/string.h b/programs/include/libc/string.h index d9ff799..ba815d6 100644 --- a/programs/include/libc/string.h +++ b/programs/include/libc/string.h @@ -31,6 +31,9 @@ char *strpbrk(const char *s, const char *accept); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n); int strcoll(const char *s1, const char *s2); +size_t strxfrm(char *dest, const char *src, size_t n); +char *strtok(char *str, const char *delim); +char *strtok_r(char *str, const char *delim, char **saveptr); char *strstr(const char *haystack, const char *needle); const char *strerror(int errnum); diff --git a/programs/include/libc/wchar.h b/programs/include/libc/wchar.h index 70154b8..cd90739 100644 --- a/programs/include/libc/wchar.h +++ b/programs/include/libc/wchar.h @@ -11,6 +11,11 @@ extern "C" { typedef unsigned int wint_t; +/* Opaque shift state for the (byte-oriented) C locale. */ +typedef struct { + unsigned long __opaque; +} mbstate_t; + #define WEOF ((wint_t)-1) /* Byte-oriented C locale only: one byte, one character. */ diff --git a/programs/lib/libc/crt/crt1.c b/programs/lib/libc/crt/crt1.c index 0a16c6a..a84d705 100644 --- a/programs/lib/libc/crt/crt1.c +++ b/programs/lib/libc/crt/crt1.c @@ -36,6 +36,38 @@ static inline long _sys2(long nr, long a1, long a2) { extern int main(int argc, char** argv); +/* Global constructor/destructor tables. The linker provides these + boundary symbols whenever .preinit_array/.init_array/.fini_array + sections exist; the weak references make them all optional so plain + C programs keep working unchanged. Frame registration for C++ + exceptions (crtbegin's frame_dummy) also arrives via .init_array. */ +extern void (*__preinit_array_start[])(void) __attribute__((weak)); +extern void (*__preinit_array_end[])(void) __attribute__((weak)); +extern void (*__init_array_start[])(void) __attribute__((weak)); +extern void (*__init_array_end[])(void) __attribute__((weak)); +extern void (*__fini_array_start[])(void) __attribute__((weak)); +extern void (*__fini_array_end[])(void) __attribute__((weak)); + +extern int atexit(void (*fn)(void)); +extern void exit(int status); + +static void _run_init_arrays(void) { + for (void (**f)(void) = __preinit_array_start; f < __preinit_array_end; f++) { + (*f)(); + } + for (void (**f)(void) = __init_array_start; f < __init_array_end; f++) { + (*f)(); + } +} + +/* Destructors run in reverse registration order, hooked through + atexit so they also run when the program calls exit() directly. */ +static void _run_fini_array(void) { + for (void (**f)(void) = __fini_array_end; f > __fini_array_start; f--) { + (*(f - 1))(); + } +} + void _start(void) { /* Static: 4 KiB args + 256 argv slots would crowd a 32 KiB stack. */ static char argbuf[4096]; @@ -77,6 +109,13 @@ void _start(void) { argv[argc] = 0; - _sys1(SYS_EXIT, (long)main(argc, argv)); + if (__fini_array_start != __fini_array_end) { + atexit(_run_fini_array); + } + _run_init_arrays(); + + /* exit() (not raw SYS_EXIT) so the atexit chain - including the + fini-array walker registered above - runs on return from main. */ + exit(main(argc, argv)); __builtin_unreachable(); } diff --git a/programs/lib/libc/crt1.o b/programs/lib/libc/crt1.o index a95f5da..4b057b8 100644 Binary files a/programs/lib/libc/crt1.o and b/programs/lib/libc/crt1.o differ diff --git a/programs/lib/libc/libc.c b/programs/lib/libc/libc.c index 79c86e1..8d3c821 100644 --- a/programs/lib/libc/libc.c +++ b/programs/lib/libc/libc.c @@ -3148,6 +3148,48 @@ unsigned int sleep(unsigned int seconds) { return 0; } +/* ======================================================================== + math.h: modf and C99 float variants (wrappers over the double + implementations; assumed present by hosted libstdc++) + ======================================================================== */ + +double modf(double x, double *iptr) { + double i = (x < 0.0) ? ceil(x) : floor(x); + *iptr = i; + return x - i; +} + +float modff(float x, float *iptr) { + double di; + double r = modf((double)x, &di); + *iptr = (float)di; + return (float)r; +} + +double hypot(double x, double y) { + return sqrt(x * x + y * y); +} + +float hypotf(float x, float y) { + return (float)hypot((double)x, (double)y); +} + +float acosf(float x) { return (float)acos((double)x); } +float asinf(float x) { return (float)asin((double)x); } +float atanf(float x) { return (float)atan((double)x); } +float atan2f(float y, float x) { return (float)atan2((double)y, (double)x); } +float coshf(float x) { return (float)cosh((double)x); } +float expf(float x) { return (float)exp((double)x); } +float fmodf(float x, float y) { return (float)fmod((double)x, (double)y); } +float frexpf(float x, int *e) { return (float)frexp((double)x, e); } +float ldexpf(float x, int e) { return (float)ldexp((double)x, e); } +float logf(float x) { return (float)log((double)x); } +float log10f(float x) { return (float)log10((double)x); } +float powf(float x, float y) { return (float)pow((double)x, (double)y); } +float sinhf(float x) { return (float)sinh((double)x); } +float tanf(float x) { return (float)tan((double)x); } +float tanhf(float x) { return (float)tanh((double)x); } + /* ======================================================================== POSIX additions for hosted programs (native binutils/gcc ports) ======================================================================== */ @@ -3232,6 +3274,61 @@ size_t mbstowcs(wchar_t *dst, const char *src, size_t n) { return i; } +/* Byte-oriented C locale: one byte, one character. */ +int mbtowc(wchar_t *pwc, const char *s, size_t n) { + if (s == NULL) return 0; /* no shift states */ + if (n == 0) return -1; + if (*s == '\0') { + if (pwc != NULL) *pwc = 0; + return 0; + } + if (pwc != NULL) *pwc = (wchar_t)(unsigned char)*s; + return 1; +} + +int wctomb(char *s, wchar_t wc) { + if (s == NULL) return 0; + *s = (char)wc; + return 1; +} + +char *strtok_r(char *str, const char *delim, char **saveptr) { + char *s = (str != NULL) ? str : *saveptr; + if (s == NULL) { + return NULL; + } + s += strspn(s, delim); + if (*s == '\0') { + *saveptr = NULL; + return NULL; + } + char *tok = s; + s += strcspn(s, delim); + if (*s != '\0') { + *s++ = '\0'; + *saveptr = s; + } else { + *saveptr = NULL; + } + return tok; +} + +char *strtok(char *str, const char *delim) { + static char *_strtok_save; + return strtok_r(str, delim, &_strtok_save); +} + +/* No collation beyond byte order in the C locale: transform = copy. */ +size_t strxfrm(char *dest, const char *src, size_t n) { + size_t len = strlen(src); + if (n > 0) { + size_t c = (len < n - 1) ? len : n - 1; + memcpy(dest, src, c); + dest[c] = '\0'; + } + return len; +} + int mblen(const char *s, size_t n) { if (s == NULL) return 0; if (n == 0) return -1; @@ -3316,6 +3413,180 @@ char *realpath(const char *path, char *resolved) { return resolved; } +int fgetpos(FILE *stream, fpos_t *pos) { + if (stream == NULL || pos == NULL) { + errno = EINVAL; + return -1; + } + long p = ftell(stream); + if (p < 0) return -1; + *pos = (fpos_t)p; + return 0; +} + +int fsetpos(FILE *stream, const fpos_t *pos) { + if (stream == NULL || pos == NULL) { + errno = EINVAL; + return -1; + } + return fseek(stream, (long)*pos, SEEK_SET); +} + +/* Streams are unbuffered in this libc; buffering hints are no-ops. */ +void setbuf(FILE *stream, char *buf) { + (void)stream; (void)buf; +} + +/* Character-driven scanf core over FILE (fgetc/ungetc). Supports the + same conversions as sscanf: %d %i %u %x %s %c %% with field widths + and the l/ll length modifiers. */ +int vfscanf(FILE *stream, const char *fmt, va_list ap) { + int matched = 0; + int c; + + while (*fmt) { + if (isspace((unsigned char)*fmt)) { + /* Whitespace in the format skips any input whitespace. */ + do { + c = fgetc(stream); + } while (c != EOF && isspace(c)); + if (c != EOF) ungetc(c, stream); + fmt++; + continue; + } + + if (*fmt != '%') { + c = fgetc(stream); + if (c == EOF) goto done; + if ((char)c != *fmt) { + ungetc(c, stream); + goto done; + } + fmt++; + continue; + } + + fmt++; + if (*fmt == '%') { + c = fgetc(stream); + if (c != '%') { + if (c != EOF) ungetc(c, stream); + goto done; + } + fmt++; + continue; + } + + int width = 0; + while (isdigit((unsigned char)*fmt)) { + width = width * 10 + (*fmt++ - '0'); + } + int is_long = 0; + if (*fmt == 'l') { is_long = 1; fmt++; } + if (*fmt == 'l') { fmt++; } /* ll behaves as l on LP64 */ + + if (*fmt == 'd' || *fmt == 'i' || *fmt == 'u' || *fmt == 'x' || + *fmt == 'X') { + int base = (*fmt == 'x' || *fmt == 'X') ? 16 : 10; + do { + c = fgetc(stream); + } while (c != EOF && isspace(c)); + if (c == EOF) goto done; + + int neg = 0; + int n = 0; + if ((*fmt == 'd' || *fmt == 'i') && (c == '-' || c == '+')) { + neg = (c == '-'); + c = fgetc(stream); + } + if (base == 16 && c == '0') { + int c2 = fgetc(stream); + if (c2 == 'x' || c2 == 'X') { + c = fgetc(stream); + } else if (c2 != EOF) { + ungetc(c2, stream); + } + } + + unsigned long val = 0; + int any = 0; + for (;;) { + int digit = -1; + if (c >= '0' && c <= '9') digit = c - '0'; + else if (base == 16 && c >= 'a' && c <= 'f') digit = c - 'a' + 10; + else if (base == 16 && c >= 'A' && c <= 'F') digit = c - 'A' + 10; + if (digit < 0 || (width > 0 && n >= width)) break; + val = val * (unsigned long)base + (unsigned long)digit; + any = 1; + n++; + c = fgetc(stream); + if (c == EOF) break; + } + if (c != EOF) ungetc(c, stream); + if (!any) goto done; + + if (*fmt == 'd' || *fmt == 'i') { + long sv = neg ? -(long)val : (long)val; + if (is_long) *va_arg(ap, long *) = sv; + else *va_arg(ap, int *) = (int)sv; + } else { + if (is_long) *va_arg(ap, unsigned long *) = val; + else *va_arg(ap, unsigned int *) = (unsigned int)val; + } + matched++; + } else if (*fmt == 's') { + do { + c = fgetc(stream); + } while (c != EOF && isspace(c)); + if (c == EOF) goto done; + char *dst = va_arg(ap, char *); + int n = 0; + while (c != EOF && !isspace(c) && (width == 0 || n < width)) { + *dst++ = (char)c; + n++; + c = fgetc(stream); + } + *dst = '\0'; + if (c != EOF) ungetc(c, stream); + if (n == 0) goto done; + matched++; + } else if (*fmt == 'c') { + int n = (width > 0) ? width : 1; + char *dst = va_arg(ap, char *); + int got = 0; + while (got < n) { + c = fgetc(stream); + if (c == EOF) break; + dst[got++] = (char)c; + } + if (got < n) goto done; + matched++; + } else { + goto done; /* unsupported conversion */ + } + fmt++; + } + +done: + return (matched == 0) ? EOF : matched; +} + +int fscanf(FILE *stream, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int r = vfscanf(stream, fmt, ap); + va_end(ap); + return r; +} + +int scanf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int r = vfscanf(stdin, fmt, ap); + va_end(ap); + return r; +} + int putc(int c, FILE *stream) { return fputc(c, stream); } diff --git a/programs/lib/libc/obj/libc.o b/programs/lib/libc/obj/libc.o index adcb219..68c9965 100644 Binary files a/programs/lib/libc/obj/libc.o and b/programs/lib/libc/obj/libc.o differ diff --git a/programs/lib/libjpeg/libjpeg.a b/programs/lib/libjpeg/libjpeg.a index a61cc2f..3ff607a 100644 Binary files a/programs/lib/libjpeg/libjpeg.a and b/programs/lib/libjpeg/libjpeg.a differ diff --git a/programs/lib/libjpegwrite/libjpegwrite.a b/programs/lib/libjpegwrite/libjpegwrite.a index 7ec51c2..e131ac3 100644 Binary files a/programs/lib/libjpegwrite/libjpegwrite.a and b/programs/lib/libjpegwrite/libjpegwrite.a differ diff --git a/programs/lib/tls/libtls.a b/programs/lib/tls/libtls.a index fa96053..00aef42 100644 Binary files a/programs/lib/tls/libtls.a and b/programs/lib/tls/libtls.a differ diff --git a/toolchain/build-montauk-toolchain.sh b/toolchain/build-montauk-toolchain.sh index 12e2581..f508783 100755 --- a/toolchain/build-montauk-toolchain.sh +++ b/toolchain/build-montauk-toolchain.sh @@ -128,10 +128,15 @@ cp "${REPO_DIR}/programs/lib/libc/crt1.o" \ "${REPO_DIR}/programs/lib/libc/crti.o" \ "${REPO_DIR}/programs/lib/libc/crtn.o" "${SYSROOT}/usr/lib/" cp "${REPO_DIR}/programs/lib/libc/liblibc.a" "${SYSROOT}/usr/lib/libc.a" -# Empty stand-ins: math functions live inside libc.a, and there is no -# libstdc++ yet, but the g++ driver links -lstdc++ -lm unconditionally. +# libm is an empty stand-in (math lives inside libc.a; the g++ driver +# links -lm unconditionally). libstdc++ is copied from the toolchain +# once built - the empty stand-in only bridges the first bootstrap. ar rcs "${SYSROOT}/usr/lib/libm.a" -ar rcs "${SYSROOT}/usr/lib/libstdc++.a" +if [[ -f "${PREFIX}/x86_64-montauk/lib/libstdc++.a" ]]; then + cp "${PREFIX}/x86_64-montauk/lib/libstdc++.a" "${SYSROOT}/usr/lib/libstdc++.a" +else + ar rcs "${SYSROOT}/usr/lib/libstdc++.a" +fi green "Sysroot ready." # ── Build Binutils ──────────────────────────────────────────────────────────── @@ -177,13 +182,19 @@ else --disable-multilib \ --disable-gcov \ --with-gnu-as \ - --with-gnu-ld + --with-gnu-ld \ + --with-newlib \ + --enable-initfini-array \ + --disable-wchar_t \ + --disable-libstdcxx-pch make -j"${JOBS}" all-gcc make -j"${JOBS}" all-target-libgcc make install-gcc make install-target-libgcc + make -j"${JOBS}" all-target-libstdc++-v3 + make install-target-libstdc++-v3 ) - green "GCC installed." + green "GCC and libstdc++ installed." fi # ── Done ────────────────────────────────────────────────────────────────────── diff --git a/toolchain/files/cxx-test.cpp b/toolchain/files/cxx-test.cpp new file mode 100644 index 0000000..52d5157 --- /dev/null +++ b/toolchain/files/cxx-test.cpp @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include + +static int ctor_ran = 0; +struct Global { Global() { ctor_ran = 42; } }; +static Global g; + +int main() { + int score = 0; + if (ctor_ran == 42) { puts("global ctor: OK"); score++; } + + std::string s = "montauk"; + s += "-cxx"; + if (s == "montauk-cxx" && s.size() == 11) { puts("std::string: OK"); score++; } + + std::vector v{5, 3, 1, 4, 2}; + std::sort(v.begin(), v.end()); + if (v.front() == 1 && v.back() == 5) { puts("std::vector+sort: OK"); score++; } + + auto p = std::make_unique("heap"); + if (*p == "heap") { puts("unique_ptr/new: OK"); score++; } + + try { + throw std::runtime_error("caught!"); + } catch (const std::exception& e) { + if (std::string(e.what()) == "caught!") { puts("exceptions: OK"); score++; } + } + + printf("%d/5 passed\n", score); + return (score == 5) ? 0 : 1; +} diff --git a/toolchain/patches/gcc-14.2.0-montauk.patch b/toolchain/patches/gcc-14.2.0-montauk.patch index cb6a0f0..827c69f 100644 --- a/toolchain/patches/gcc-14.2.0-montauk.patch +++ b/toolchain/patches/gcc-14.2.0-montauk.patch @@ -47,3 +47,15 @@ *-musl* ) # IF there is no include fixing, # THEN create a no-op fixer and exit +--- a/libstdc++-v3/configure ++++ b/libstdc++-v3/configure +@@ -27340,6 +27340,9 @@ + # on a hosted environment. + if test "x${with_newlib}" = "xyes"; then + os_include_dir="os/newlib" ++ case "${host}" in ++ *-montauk*) os_include_dir="os/generic" ;; # Montauk libc is not newlib ++ esac + $as_echo "#define HAVE_HYPOT 1" >>confdefs.h + +