feat: hosted libstdc++ for x86_64-montauk - full C++ on MontaukOS

The cross toolchain now builds real hosted C++: std::string, vector,
unique_ptr, iostreams and exceptions all work in a plain
x86_64-montauk-g++ invocation with no special flags. This is the
library that native cc1plus will link against for the GCC self-host.

crt1 now runs .preinit_array/.init_array before main and hooks
.fini_array through atexit (main returns via exit(), so destructors
run on both exit paths). Global constructors and libgcc's eh_frame
registration both ride this. GCC is reconfigured with
--enable-initfini-array (modern arrays instead of .ctors),
--with-newlib (satisfies libstdc++'s crossconfig for unknown hosts),
--disable-wchar_t and --disable-libstdcxx-pch; the montauk gcc patch
grows a hunk keeping os/generic ctype under --with-newlib (the
Montauk libc is not newlib).

libc additions harvested by the libstdc++ build: mbtowc/wctomb,
strxfrm, strtok/strtok_r, a real vfscanf/fscanf/scanf (character-
driven with widths and l/ll; also unblocks gprof later), fgetpos/
fsetpos/setbuf/fpos_t, modf plus the C99 float math variants
(acosf..tanhf, hypot/hypotf as wrappers), FP_* classification macros,
the full POSIX errno vocabulary, complete DT_* dirent types, and
mbstate_t.

The SDK ships the real libstdc++.a (was an empty stand-in), the C++
headers at 0:/sdk/include/c++, and cxx-test.elf (global ctor, string,
vector+sort, unique_ptr, throw/catch; exits 0 on 5/5). Ramdisk sits
at 1571 files, within the 2048 table.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-16 23:06:50 +02:00
co-authored by Claude Fable 5
parent eadc79708f
commit 03d1a1bc2f
19 changed files with 480 additions and 8 deletions
+40 -1
View File
@@ -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();
}
Binary file not shown.
+271
View File
@@ -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);
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.