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:
@@ -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.
@@ -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.
Reference in New Issue
Block a user