diff --git a/.gitignore b/.gitignore index fbcd4db..d7e911e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ programs/obj/ programs/src/*/obj/ programs/libs/*/obj/ programs/lib/libc/liblibc.a +programs/lib/libc/liblibc-full.a +programs/lib/libc/obj/libc-merged.o programs/lib/libloader/liblibloader.a programs/gui/icons/ CLAUDE.md diff --git a/programs/GNUmakefile b/programs/GNUmakefile index 69b8f7b..856c839 100644 --- a/programs/GNUmakefile +++ b/programs/GNUmakefile @@ -105,8 +105,24 @@ lib/bearssl/build/libbearssl.a: bearssl: lib/bearssl/build/libbearssl.a # Build shared libc static library. -libc: - $(MAKE) -C lib/libc +# +# The archives and CRT objects get a real file rule, not just the `libc` +# phony, so that programs can list them as prerequisites and actually relink +# when libc changes. A phony prerequisite would instead make every program +# permanently out of date; no prerequisite at all (the old state) both races +# under -j and silently leaves programs linked against a stale libc. +# Grouped target (&:, GNU make 4.3+): one sub-make produces all of them. +LIBC_DIR := lib/libc +LIBC_LIB := $(LIBC_DIR)/liblibc.a +LIBC_SRCS := $(wildcard $(LIBC_DIR)/*.c $(LIBC_DIR)/*.h $(LIBC_DIR)/*.S \ + $(LIBC_DIR)/crt/*.c) $(LIBC_DIR)/Makefile +LIBC_OUTPUTS := $(LIBC_LIB) $(LIBC_DIR)/liblibc-full.a \ + $(LIBC_DIR)/crt1.o $(LIBC_DIR)/crti.o $(LIBC_DIR)/crtn.o + +$(LIBC_OUTPUTS) &: $(LIBC_SRCS) + $(MAKE) -C $(LIBC_DIR) + +libc: $(LIBC_OUTPUTS) # Build shared TLS helper library. tls: bearssl libc @@ -144,8 +160,8 @@ libjpegwrite: libc imageviewer: libc libjpeg $(MAKE) -C src/imageviewer -# Build fontpreview standalone GUI client (no library deps). -fontpreview: +# Build fontpreview standalone GUI client (links libc). +fontpreview: libc $(MAKE) -C src/fontpreview # Build spreadsheet standalone GUI client (depends on libc and libdialogs). @@ -271,8 +287,9 @@ mandelbrot: libc rpgdemo: libc $(MAKE) -C src/rpgdemo -# Build doom via its own Makefile (depends on libc). -doom: libc +# Build doom via its own Makefile (depends on libc and the shared-lib loader, +# both of which it links). +doom: libc libloader $(MAKE) -C src/doom # Build screenshot tool (depends on libc, libjpegwrite, libloader, and libdialogs). @@ -369,10 +386,10 @@ $(BINDIR)/os/wallpapers/%: $(WPDIR)/% cp $< $@ # Build each system program from its source files. -$(BINDIR)/os/%.elf: src/%/main.cpp link.ld GNUmakefile +$(BINDIR)/os/%.elf: src/%/main.cpp link.ld GNUmakefile $(LIBC_LIB) mkdir -p $(BINDIR)/os obj/$* $(CXX) $(CXXFLAGS) -c src/$*/main.cpp -o obj/$*/main.o - $(CXX) $(CXXFLAGS) $(LDFLAGS) obj/$*/main.o lib/libc/liblibc.a -o $@ + $(CXX) $(CXXFLAGS) $(LDFLAGS) obj/$*/main.o $(LIBC_LIB) -o $@ # ============================================================================ # Native development kit (Montauk SDK): binutils built for MontaukOS @@ -399,7 +416,10 @@ ifneq ($(wildcard $(NATIVE_BIN)/as),) cp -r include/montauk $(BINDIR)/sdk/include/montauk cp -r include/Api $(BINDIR)/sdk/include/Api cp -r ../kernel/freestnd-cxx-hdrs/x86_64/include/. $(BINDIR)/sdk/include/ - cp lib/libc/liblibc.a $(BINDIR)/sdk/lib/libc.a + # liblibc-full.a, not liblibc.a: on-OS builds cannot pass + # -Wl,-u,_pf_putfloat, so the sysroot ships the variant with the printf + # float path already bound in. See lib/libc/Makefile. + cp lib/libc/liblibc-full.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 cp ../toolchain/local/x86_64-montauk/lib/libstdc++.a $(BINDIR)/sdk/lib/libstdc++.a diff --git a/programs/lib/libc/Makefile b/programs/lib/libc/Makefile index 228a178..2f3b0fd 100644 --- a/programs/lib/libc/Makefile +++ b/programs/lib/libc/Makefile @@ -10,9 +10,11 @@ TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-monta ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),) CC := $(TOOLCHAIN_PREFIX)gcc AR := $(TOOLCHAIN_PREFIX)ar + LD := $(TOOLCHAIN_PREFIX)ld else CC := gcc AR := ar + LD := ld endif # ---- Paths ---- @@ -63,16 +65,25 @@ CRT_CFLAGS := \ # ---- Target ---- TARGET := liblibc.a +SDK_TARGET := liblibc-full.a CRT_OBJS := crt1.o crti.o crtn.o .PHONY: all clean -all: $(TARGET) $(CRT_OBJS) +all: $(TARGET) $(SDK_TARGET) $(CRT_OBJS) -$(TARGET): $(OBJDIR)/libc.o $(ASM_OBJS) +# printf_float.o is deliberately a SEPARATE archive member: a weak undefined +# reference does not extract an archive member, so programs that never format +# a float leave the decimal-conversion and pow/exp/log code behind. Programs +# that do need %e/%f/%g must link with -Wl,-u,_pf_putfloat to force it in. +$(TARGET): $(OBJDIR)/libc.o $(OBJDIR)/printf_float.o $(ASM_OBJS) $(AR) rcs $@ $^ -$(OBJDIR)/libc.o: libc.c Makefile +$(OBJDIR)/libc.o: libc.c printf_internal.h Makefile + @mkdir -p $(OBJDIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(OBJDIR)/printf_float.o: printf_float.c printf_internal.h Makefile @mkdir -p $(OBJDIR) $(CC) $(CFLAGS) -c $< -o $@ @@ -80,6 +91,17 @@ $(OBJDIR)/setjmp.o: setjmp.S Makefile @mkdir -p $(OBJDIR) $(CC) $(CFLAGS) -c $< -o $@ +# Variant shipped in the on-OS sysroots (0:/sdk/lib/libc.a and tcc's), where +# nobody can pass -Wl,-u,_pf_putfloat by hand. `ld -r` binds vsnprintf's weak +# reference to the float module at merge time, so %e/%f/%g just work for code +# compiled ON MontaukOS. Function-level --gc-sections still applies to the +# rest; only the float path is pinned. Image size is not a concern here. +$(SDK_TARGET): $(OBJDIR)/libc-merged.o $(ASM_OBJS) + $(AR) rcs $@ $^ + +$(OBJDIR)/libc-merged.o: $(OBJDIR)/libc.o $(OBJDIR)/printf_float.o + $(LD) -r -o $@ $(OBJDIR)/libc.o $(OBJDIR)/printf_float.o + crt1.o: $(CRTDIR)/crt1.c Makefile $(CC) $(CRT_CFLAGS) -c $< -o $@ @@ -90,4 +112,4 @@ crtn.o: $(CRTDIR)/crtn.c Makefile $(CC) $(CRT_CFLAGS) -c $< -o $@ clean: - rm -rf $(OBJDIR) $(TARGET) $(CRT_OBJS) + rm -rf $(OBJDIR) $(TARGET) $(SDK_TARGET) $(CRT_OBJS) diff --git a/programs/lib/libc/libc.c b/programs/lib/libc/libc.c index 6bd7876..536ca97 100644 --- a/programs/lib/libc/libc.c +++ b/programs/lib/libc/libc.c @@ -1150,13 +1150,17 @@ int system(const char *command) { printf family — vsnprintf core ======================================================================== */ -struct _pf_state { - char *buf; - size_t pos; - size_t max; -}; +#include "printf_internal.h" -static void _pf_putc(struct _pf_state *st, char c) { +/* Weak: resolves to 0 unless the program pulls in printf_float.o with + -Wl,-u,_pf_putfloat. See the %e/%f/%g case in vsnprintf below. */ +extern __attribute__((weak)) void _pf_putfloat(struct _pf_state *st, double v, + char conv, int precision, + int width, char pad, + int left_align, int plus, + int space, int alt); + +void _pf_putc(struct _pf_state *st, char c) { if (st->pos < st->max) st->buf[st->pos] = c; st->pos++; @@ -1208,226 +1212,6 @@ static void _pf_putnum(struct _pf_state *st, unsigned long val, int base, int up _pf_putc(st, tmp[--i]); } -/* Union for double bit manipulation (used by float formatting and math). */ -typedef union { double d; uint64_t u; } _dbl_bits; - -/* Base-10 exponent of the leading digit of a positive finite value, i.e. the - E such that value = m * 10^E with m in [1, 10). No rounding is applied. */ -static int _pf_exp10(double v) { - int E = (int)floor(log10(v)); - double s = v / pow(10.0, (double)E); - if (s >= 10.0) E++; - else if (s < 1.0) E--; - return E; -} - -/* Generate `nsig` significant decimal digits of a positive finite value into - `out` (ASCII, no terminator). Returns the base-10 exponent of the leading - digit, i.e. value ~= (out[0].out[1..]) * 10^E. Rounding is half-up and may - carry into a new leading digit, bumping the returned exponent. */ -static int _pf_gendigits(double v, int nsig, char *out) { - int E = (int)floor(log10(v)); - double scale = v / pow(10.0, (double)E); - /* Correct estimate errors so scale lands in [1, 10). */ - if (scale >= 10.0) { scale /= 10.0; E++; } - else if (scale < 1.0) { scale *= 10.0; E--; } - - for (int i = 0; i < nsig; i++) { - int d = (int)scale; - if (d < 0) d = 0; else if (d > 9) d = 9; - out[i] = (char)('0' + d); - scale = (scale - d) * 10.0; - } - - if (scale >= 5.0) { /* round up based on the next digit */ - int i = nsig - 1; - while (i >= 0) { - if (out[i] == '9') { out[i] = '0'; i--; } - else { out[i]++; break; } - } - if (i < 0) { out[0] = '1'; E++; } /* all nines carried out */ - } - return E; -} - -/* Append the two-or-more-digit exponent field of %e/%g ("e+NN"). */ -static void _pf_put_exp(char *mbuf, int *mlen, int E, int upper) { - int m = *mlen; - mbuf[m++] = upper ? 'E' : 'e'; - int ex = E; char es = '+'; - if (ex < 0) { es = '-'; ex = -ex; } - mbuf[m++] = es; - char eb[8]; int el = 0; - if (ex == 0) eb[el++] = '0'; - while (ex > 0) { eb[el++] = (char)('0' + ex % 10); ex /= 10; } - while (el < 2) eb[el++] = '0'; - while (el > 0) mbuf[m++] = eb[--el]; - *mlen = m; -} - -/* Format a floating-point value for the %e/%f/%g conversions (and uppercase - variants), honoring precision, width, padding and sign flags. */ -static void _pf_putfloat(struct _pf_state *st, double v, char conv, - int precision, int width, char pad, - int left_align, int plus, int space, int alt) { - int upper = (conv >= 'A' && conv <= 'Z'); - char lc = upper ? (char)(conv - 'A' + 'a') : conv; - - _dbl_bits bits; bits.d = v; - int neg = (int)(bits.u >> 63); - uint64_t expo = (bits.u >> 52) & 0x7FF; - uint64_t mant = bits.u & 0x000FFFFFFFFFFFFFULL; - - char sign = 0; - if (neg) sign = '-'; - else if (plus) sign = '+'; - else if (space) sign = ' '; - - /* Infinity / NaN: never zero-padded. */ - if (expo == 0x7FF) { - const char *word = mant ? (upper ? "NAN" : "nan") : (upper ? "INF" : "inf"); - if (mant) sign = 0; - char tmp[8]; int tl = 0; - if (sign) tmp[tl++] = sign; - for (const char *p = word; *p; p++) tmp[tl++] = *p; - if (!left_align) for (int w = tl; w < width; w++) _pf_putc(st, ' '); - for (int i = 0; i < tl; i++) _pf_putc(st, tmp[i]); - if (left_align) for (int w = tl; w < width; w++) _pf_putc(st, ' '); - return; - } - - if (precision < 0) precision = 6; - if (precision > 340) precision = 340; - - double av = neg ? -v : v; - - char mbuf[800]; - int mlen = 0; - - if (lc == 'e') { - int nsig = precision + 1; - char dig[400]; - int E; - if (av == 0.0) { for (int i = 0; i < nsig; i++) dig[i] = '0'; E = 0; } - else E = _pf_gendigits(av, nsig, dig); - mbuf[mlen++] = dig[0]; - if (precision > 0 || alt) { - mbuf[mlen++] = '.'; - for (int i = 1; i <= precision; i++) mbuf[mlen++] = dig[i]; - } - _pf_put_exp(mbuf, &mlen, E, upper); - } - else if (lc == 'f') { - if (av == 0.0) { - mbuf[mlen++] = '0'; - if (precision > 0 || alt) { - mbuf[mlen++] = '.'; - for (int i = 0; i < precision; i++) mbuf[mlen++] = '0'; - } - } else { - int Ef = _pf_exp10(av); - int keep = Ef + 1 + precision; - if (keep <= 0) { - char d1[4]; - _pf_gendigits(av, 1, d1); - int roundUp = (keep == 0 && d1[0] >= '5'); - mbuf[mlen++] = '0'; - if (precision > 0 || alt) { - mbuf[mlen++] = '.'; - for (int i = 0; i < precision; i++) mbuf[mlen++] = '0'; - } - if (roundUp) mbuf[mlen - 1] = '1'; - } else { - if (keep > 380) keep = 380; - char dig[400]; - int E = _pf_gendigits(av, keep, dig); - int len = keep; - int intlen = (E >= 0) ? (E + 1) : 0; - if (intlen == 0) mbuf[mlen++] = '0'; - else for (int i = 0; i < intlen; i++) mbuf[mlen++] = (i < len ? dig[i] : '0'); - if (precision > 0 || alt) { - mbuf[mlen++] = '.'; - if (E < 0) { - int lead = -E - 1; - for (int k = 0; k < precision; k++) { - if (k < lead) mbuf[mlen++] = '0'; - else { int idx = k - lead; mbuf[mlen++] = (idx < len ? dig[idx] : '0'); } - } - } else { - for (int k = 0; k < precision; k++) { - int idx = intlen + k; - mbuf[mlen++] = (idx < len ? dig[idx] : '0'); - } - } - } - } - } - } - else { /* 'g' / 'G' */ - int P = precision; if (P == 0) P = 1; - char dig[400]; - int E; - if (av == 0.0) { E = 0; for (int i = 0; i < P; i++) dig[i] = '0'; } - else E = _pf_gendigits(av, P, dig); - int len = P; - int useExp = (E < -4 || E >= P); - int hasDot = 0; - if (useExp) { - int fp = P - 1; - mbuf[mlen++] = dig[0]; - if (fp > 0 || alt) { - mbuf[mlen++] = '.'; hasDot = 1; - for (int i = 1; i <= fp; i++) mbuf[mlen++] = dig[i]; - } - if (!alt && hasDot) { - while (mbuf[mlen - 1] == '0') mlen--; - if (mbuf[mlen - 1] == '.') mlen--; - } - _pf_put_exp(mbuf, &mlen, E, upper); - } else { - int fp = P - 1 - E; - if (fp < 0) fp = 0; - int intlen = (E >= 0) ? (E + 1) : 0; - if (intlen == 0) mbuf[mlen++] = '0'; - else for (int i = 0; i < intlen; i++) mbuf[mlen++] = (i < len ? dig[i] : '0'); - if (fp > 0 || alt) { - mbuf[mlen++] = '.'; hasDot = 1; - if (E < 0) { - int lead = -E - 1; - for (int k = 0; k < fp; k++) { - if (k < lead) mbuf[mlen++] = '0'; - else { int idx = k - lead; mbuf[mlen++] = (idx < len ? dig[idx] : '0'); } - } - } else { - for (int k = 0; k < fp; k++) { - int idx = intlen + k; - mbuf[mlen++] = (idx < len ? dig[idx] : '0'); - } - } - } - if (!alt && hasDot) { - while (mbuf[mlen - 1] == '0') mlen--; - if (mbuf[mlen - 1] == '.') mlen--; - } - } - } - - int total = (sign ? 1 : 0) + mlen; - if (!left_align && pad == '0') { - if (sign) _pf_putc(st, sign); - for (int w = total; w < width; w++) _pf_putc(st, '0'); - for (int i = 0; i < mlen; i++) _pf_putc(st, mbuf[i]); - } else if (!left_align) { - for (int w = total; w < width; w++) _pf_putc(st, ' '); - if (sign) _pf_putc(st, sign); - for (int i = 0; i < mlen; i++) _pf_putc(st, mbuf[i]); - } else { - if (sign) _pf_putc(st, sign); - for (int i = 0; i < mlen; i++) _pf_putc(st, mbuf[i]); - for (int w = total; w < width; w++) _pf_putc(st, ' '); - } -} - int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) { struct _pf_state st; st.buf = buf; @@ -1579,9 +1363,18 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) { case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': { + /* Consume the argument either way: skipping it would desync every + later conversion in the same format string. */ double dv = va_arg(ap, double); - _pf_putfloat(&st, dv, *fmt, precision, width, pad, - left_align, plus, space, alt); + if (_pf_putfloat) { + _pf_putfloat(&st, dv, *fmt, precision, width, pad, + left_align, plus, space, alt); + } else { + /* printf_float.o was not linked in; echo the spec so the gap + is visible rather than silently printing a wrong number. */ + _pf_putc(&st, '%'); + _pf_putc(&st, *fmt); + } break; } case '%': diff --git a/programs/lib/libc/obj/libc.o b/programs/lib/libc/obj/libc.o index addf4aa..837de66 100644 Binary files a/programs/lib/libc/obj/libc.o and b/programs/lib/libc/obj/libc.o differ diff --git a/programs/lib/libc/obj/printf_float.o b/programs/lib/libc/obj/printf_float.o new file mode 100644 index 0000000..a7bbae9 Binary files /dev/null and b/programs/lib/libc/obj/printf_float.o differ diff --git a/programs/lib/libc/printf_float.c b/programs/lib/libc/printf_float.c new file mode 100644 index 0000000..4c80644 --- /dev/null +++ b/programs/lib/libc/printf_float.c @@ -0,0 +1,233 @@ +/* + * printf_float.c + * Floating-point conversions (%e/%f/%g and uppercase variants) for the + * printf family. Kept in its own translation unit -- and therefore its own + * archive member -- so that --gc-sections can drop it, along with the + * pow/exp/log it drags in, from the many programs that only ever format + * integers and strings. vsnprintf reaches this through a weak reference; + * see printf_internal.h and lib/libc/Makefile. + * Copyright (c) 2026 Daniel Hammer +*/ + +#include +#include +#include + +#include "printf_internal.h" + +/* Base-10 exponent of the leading digit of a positive finite value, i.e. the + E such that value = m * 10^E with m in [1, 10). No rounding is applied. */ +static int _pf_exp10(double v) { + int E = (int)floor(log10(v)); + double s = v / pow(10.0, (double)E); + if (s >= 10.0) E++; + else if (s < 1.0) E--; + return E; +} + +/* Generate `nsig` significant decimal digits of a positive finite value into + `out` (ASCII, no terminator). Returns the base-10 exponent of the leading + digit, i.e. value ~= (out[0].out[1..]) * 10^E. Rounding is half-up and may + carry into a new leading digit, bumping the returned exponent. */ +static int _pf_gendigits(double v, int nsig, char *out) { + int E = (int)floor(log10(v)); + double scale = v / pow(10.0, (double)E); + /* Correct estimate errors so scale lands in [1, 10). */ + if (scale >= 10.0) { scale /= 10.0; E++; } + else if (scale < 1.0) { scale *= 10.0; E--; } + + for (int i = 0; i < nsig; i++) { + int d = (int)scale; + if (d < 0) d = 0; else if (d > 9) d = 9; + out[i] = (char)('0' + d); + scale = (scale - d) * 10.0; + } + + if (scale >= 5.0) { /* round up based on the next digit */ + int i = nsig - 1; + while (i >= 0) { + if (out[i] == '9') { out[i] = '0'; i--; } + else { out[i]++; break; } + } + if (i < 0) { out[0] = '1'; E++; } /* all nines carried out */ + } + return E; +} + +/* Append the two-or-more-digit exponent field of %e/%g ("e+NN"). */ +static void _pf_put_exp(char *mbuf, int *mlen, int E, int upper) { + int m = *mlen; + mbuf[m++] = upper ? 'E' : 'e'; + int ex = E; char es = '+'; + if (ex < 0) { es = '-'; ex = -ex; } + mbuf[m++] = es; + char eb[8]; int el = 0; + if (ex == 0) eb[el++] = '0'; + while (ex > 0) { eb[el++] = (char)('0' + ex % 10); ex /= 10; } + while (el < 2) eb[el++] = '0'; + while (el > 0) mbuf[m++] = eb[--el]; + *mlen = m; +} + +/* Format a floating-point value for the %e/%f/%g conversions (and uppercase + variants), honoring precision, width, padding and sign flags. */ +void _pf_putfloat(struct _pf_state *st, double v, char conv, + int precision, int width, char pad, + int left_align, int plus, int space, int alt) { + int upper = (conv >= 'A' && conv <= 'Z'); + char lc = upper ? (char)(conv - 'A' + 'a') : conv; + + _dbl_bits bits; bits.d = v; + int neg = (int)(bits.u >> 63); + uint64_t expo = (bits.u >> 52) & 0x7FF; + uint64_t mant = bits.u & 0x000FFFFFFFFFFFFFULL; + + char sign = 0; + if (neg) sign = '-'; + else if (plus) sign = '+'; + else if (space) sign = ' '; + + /* Infinity / NaN: never zero-padded. */ + if (expo == 0x7FF) { + const char *word = mant ? (upper ? "NAN" : "nan") : (upper ? "INF" : "inf"); + if (mant) sign = 0; + char tmp[8]; int tl = 0; + if (sign) tmp[tl++] = sign; + for (const char *p = word; *p; p++) tmp[tl++] = *p; + if (!left_align) for (int w = tl; w < width; w++) _pf_putc(st, ' '); + for (int i = 0; i < tl; i++) _pf_putc(st, tmp[i]); + if (left_align) for (int w = tl; w < width; w++) _pf_putc(st, ' '); + return; + } + + if (precision < 0) precision = 6; + if (precision > 340) precision = 340; + + double av = neg ? -v : v; + + char mbuf[800]; + int mlen = 0; + + if (lc == 'e') { + int nsig = precision + 1; + char dig[400]; + int E; + if (av == 0.0) { for (int i = 0; i < nsig; i++) dig[i] = '0'; E = 0; } + else E = _pf_gendigits(av, nsig, dig); + mbuf[mlen++] = dig[0]; + if (precision > 0 || alt) { + mbuf[mlen++] = '.'; + for (int i = 1; i <= precision; i++) mbuf[mlen++] = dig[i]; + } + _pf_put_exp(mbuf, &mlen, E, upper); + } + else if (lc == 'f') { + if (av == 0.0) { + mbuf[mlen++] = '0'; + if (precision > 0 || alt) { + mbuf[mlen++] = '.'; + for (int i = 0; i < precision; i++) mbuf[mlen++] = '0'; + } + } else { + int Ef = _pf_exp10(av); + int keep = Ef + 1 + precision; + if (keep <= 0) { + char d1[4]; + _pf_gendigits(av, 1, d1); + int roundUp = (keep == 0 && d1[0] >= '5'); + mbuf[mlen++] = '0'; + if (precision > 0 || alt) { + mbuf[mlen++] = '.'; + for (int i = 0; i < precision; i++) mbuf[mlen++] = '0'; + } + if (roundUp) mbuf[mlen - 1] = '1'; + } else { + if (keep > 380) keep = 380; + char dig[400]; + int E = _pf_gendigits(av, keep, dig); + int len = keep; + int intlen = (E >= 0) ? (E + 1) : 0; + if (intlen == 0) mbuf[mlen++] = '0'; + else for (int i = 0; i < intlen; i++) mbuf[mlen++] = (i < len ? dig[i] : '0'); + if (precision > 0 || alt) { + mbuf[mlen++] = '.'; + if (E < 0) { + int lead = -E - 1; + for (int k = 0; k < precision; k++) { + if (k < lead) mbuf[mlen++] = '0'; + else { int idx = k - lead; mbuf[mlen++] = (idx < len ? dig[idx] : '0'); } + } + } else { + for (int k = 0; k < precision; k++) { + int idx = intlen + k; + mbuf[mlen++] = (idx < len ? dig[idx] : '0'); + } + } + } + } + } + } + else { /* 'g' / 'G' */ + int P = precision; if (P == 0) P = 1; + char dig[400]; + int E; + if (av == 0.0) { E = 0; for (int i = 0; i < P; i++) dig[i] = '0'; } + else E = _pf_gendigits(av, P, dig); + int len = P; + int useExp = (E < -4 || E >= P); + int hasDot = 0; + if (useExp) { + int fp = P - 1; + mbuf[mlen++] = dig[0]; + if (fp > 0 || alt) { + mbuf[mlen++] = '.'; hasDot = 1; + for (int i = 1; i <= fp; i++) mbuf[mlen++] = dig[i]; + } + if (!alt && hasDot) { + while (mbuf[mlen - 1] == '0') mlen--; + if (mbuf[mlen - 1] == '.') mlen--; + } + _pf_put_exp(mbuf, &mlen, E, upper); + } else { + int fp = P - 1 - E; + if (fp < 0) fp = 0; + int intlen = (E >= 0) ? (E + 1) : 0; + if (intlen == 0) mbuf[mlen++] = '0'; + else for (int i = 0; i < intlen; i++) mbuf[mlen++] = (i < len ? dig[i] : '0'); + if (fp > 0 || alt) { + mbuf[mlen++] = '.'; hasDot = 1; + if (E < 0) { + int lead = -E - 1; + for (int k = 0; k < fp; k++) { + if (k < lead) mbuf[mlen++] = '0'; + else { int idx = k - lead; mbuf[mlen++] = (idx < len ? dig[idx] : '0'); } + } + } else { + for (int k = 0; k < fp; k++) { + int idx = intlen + k; + mbuf[mlen++] = (idx < len ? dig[idx] : '0'); + } + } + } + if (!alt && hasDot) { + while (mbuf[mlen - 1] == '0') mlen--; + if (mbuf[mlen - 1] == '.') mlen--; + } + } + } + + int total = (sign ? 1 : 0) + mlen; + if (!left_align && pad == '0') { + if (sign) _pf_putc(st, sign); + for (int w = total; w < width; w++) _pf_putc(st, '0'); + for (int i = 0; i < mlen; i++) _pf_putc(st, mbuf[i]); + } else if (!left_align) { + for (int w = total; w < width; w++) _pf_putc(st, ' '); + if (sign) _pf_putc(st, sign); + for (int i = 0; i < mlen; i++) _pf_putc(st, mbuf[i]); + } else { + if (sign) _pf_putc(st, sign); + for (int i = 0; i < mlen; i++) _pf_putc(st, mbuf[i]); + for (int w = total; w < width; w++) _pf_putc(st, ' '); + } +} diff --git a/programs/lib/libc/printf_internal.h b/programs/lib/libc/printf_internal.h new file mode 100644 index 0000000..281b318 --- /dev/null +++ b/programs/lib/libc/printf_internal.h @@ -0,0 +1,39 @@ +/* + * printf_internal.h + * Shared state between the integer printf core (libc.c) and the optional + * floating-point conversion module (printf_float.c). + * Copyright (c) 2026 Daniel Hammer +*/ + +#ifndef _LIBC_PRINTF_INTERNAL_H +#define _LIBC_PRINTF_INTERNAL_H + +#include +#include + +/* Output cursor shared by every conversion. `pos` counts the characters a + conversion WOULD have written, so it may run past `max`; only writes below + `max` land in `buf`. That is what gives snprintf its C99 return value. */ +struct _pf_state { + char *buf; + size_t pos; + size_t max; +}; + +/* Defined in libc.c. External rather than static so the float module can + reach it; it is the only core helper the float conversions need. */ +void _pf_putc(struct _pf_state *st, char c); + +/* Union for double bit manipulation (used by float formatting and math). */ +typedef union { double d; uint64_t u; } _dbl_bits; + +/* Defined in printf_float.c, which is a SEPARATE archive member so that + --gc-sections can drop the decimal-conversion and pow/exp/log code from + programs that never format a float. vsnprintf calls this through a WEAK + reference: link with -Wl,-u,_pf_putfloat to pull the module in. Without + that flag %e/%f/%g echo their conversion spec instead of a number. */ +void _pf_putfloat(struct _pf_state *st, double v, char conv, + int precision, int width, char pad, + int left_align, int plus, int space, int alt); + +#endif /* _LIBC_PRINTF_INTERNAL_H */ diff --git a/programs/lib/libjpeg/libjpeg.a b/programs/lib/libjpeg/libjpeg.a index 3ff607a..1b4f728 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 e131ac3..a4ca89b 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 3143169..b4e9ae8 100644 Binary files a/programs/lib/tls/libtls.a and b/programs/lib/tls/libtls.a differ diff --git a/programs/lib/tls/obj/tls.o b/programs/lib/tls/obj/tls.o index d023fae..5eccf5d 100644 Binary files a/programs/lib/tls/obj/tls.o and b/programs/lib/tls/obj/tls.o differ diff --git a/programs/src/audio/Makefile b/programs/src/audio/Makefile index 854a4be..429c286 100644 --- a/programs/src/audio/Makefile +++ b/programs/src/audio/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/audio/audio.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/audio $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/bluetooth/Makefile b/programs/src/bluetooth/Makefile index 2a96b53..e1c1533 100644 --- a/programs/src/bluetooth/Makefile +++ b/programs/src/bluetooth/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/bluetooth/bluetooth.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/bluetooth $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/calculator/Makefile b/programs/src/calculator/Makefile index a217617..b6d94e2 100644 --- a/programs/src/calculator/Makefile +++ b/programs/src/calculator/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/calculator/calculator.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/calculator $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/charmap/Makefile b/programs/src/charmap/Makefile index 94f4674..0d52d54 100644 --- a/programs/src/charmap/Makefile +++ b/programs/src/charmap/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/charmap/charmap.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/charmap $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/devexplorer/Makefile b/programs/src/devexplorer/Makefile index 691dbd8..640e09d 100644 --- a/programs/src/devexplorer/Makefile +++ b/programs/src/devexplorer/Makefile @@ -63,7 +63,7 @@ TARGET := $(BINDIR)/apps/devexplorer/devexplorer.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/devexplorer $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/dhcp/main.cpp b/programs/src/dhcp/main.cpp index 1a8c05a..798feac 100644 --- a/programs/src/dhcp/main.cpp +++ b/programs/src/dhcp/main.cpp @@ -7,96 +7,11 @@ #include #include +#include using montauk::memcpy; using montauk::memset; -// ---- Minimal snprintf (no libc available) ---- - -using va_list = __builtin_va_list; -#define va_start __builtin_va_start -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg - -struct PfState { - char* buf; - int pos; - int max; -}; - -static void pf_putc(PfState* st, char c) { - if (st->pos < st->max) st->buf[st->pos] = c; - st->pos++; -} - -static void pf_putnum(PfState* st, unsigned long val, int base, int width, char pad, int neg) { - char tmp[24]; - int i = 0; - const char* digits = "0123456789abcdef"; - if (val == 0) { tmp[i++] = '0'; } - else { while (val > 0) { tmp[i++] = digits[val % base]; val /= base; } } - int total = (neg ? 1 : 0) + i; - if (neg && pad == '0') pf_putc(st, '-'); - for (int w = total; w < width; w++) pf_putc(st, pad); - if (neg && pad != '0') pf_putc(st, '-'); - while (i > 0) pf_putc(st, tmp[--i]); -} - -static int vsnprintf(char* buf, int size, const char* fmt, va_list ap) { - PfState st; - st.buf = buf; - st.pos = 0; - st.max = size > 0 ? size - 1 : 0; - while (*fmt) { - if (*fmt != '%') { pf_putc(&st, *fmt++); continue; } - fmt++; - char pad = ' '; - if (*fmt == '0') { pad = '0'; fmt++; } - int width = 0; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } - if (*fmt == 'l') fmt++; - switch (*fmt) { - case 'd': case 'i': { - long val = va_arg(ap, int); - int neg = 0; unsigned long uval; - if (val < 0) { neg = 1; uval = (unsigned long)(-val); } - else uval = (unsigned long)val; - pf_putnum(&st, uval, 10, width, pad, neg); - break; - } - case 'u': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 10, width, pad, 0); break; } - case 'x': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 16, width, pad, 0); break; } - case 's': { - const char* s = va_arg(ap, const char*); - if (!s) s = "(null)"; - int slen = 0; while (s[slen]) slen++; - for (int w = slen; w < width; w++) pf_putc(&st, ' '); - for (int j = 0; j < slen; j++) pf_putc(&st, s[j]); - break; - } - case 'c': { char c = (char)va_arg(ap, int); pf_putc(&st, c); break; } - case '%': pf_putc(&st, '%'); break; - default: pf_putc(&st, '%'); pf_putc(&st, *fmt); break; - } - if (*fmt) fmt++; - } - if (size > 0) { - if (st.pos < size) st.buf[st.pos] = '\0'; - else st.buf[size - 1] = '\0'; - } - return st.pos; -} - -static int snprintf(char* buf, int size, const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - int ret = vsnprintf(buf, size, fmt, ap); - va_end(ap); - return ret; -} - - - // ---- DHCP constants ---- static constexpr uint8_t BOOTREQUEST = 1; diff --git a/programs/src/disks/Makefile b/programs/src/disks/Makefile index fef07a3..bd01ba0 100644 --- a/programs/src/disks/Makefile +++ b/programs/src/disks/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/disks/disks.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/disks $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/doom/Makefile b/programs/src/doom/Makefile index cd02322..9ec14e2 100644 --- a/programs/src/doom/Makefile +++ b/programs/src/doom/Makefile @@ -185,7 +185,7 @@ TARGET := $(BINDIR)/apps/doom/doom.elf all: $(TARGET) -$(TARGET): $(ALL_OBJS) $(LINK_LD) Makefile +$(TARGET): $(ALL_OBJS) $(LINK_LD) Makefile $(LOADER_LIB)/liblibloader.a $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/doom $(LD) $(CFLAGS) $(LDFLAGS) $(ALL_OBJS) $(LOADER_LIB)/liblibloader.a $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/httpd/main.cpp b/programs/src/httpd/main.cpp index 6103dab..2a048e6 100644 --- a/programs/src/httpd/main.cpp +++ b/programs/src/httpd/main.cpp @@ -8,96 +8,13 @@ #include #include +#include using montauk::slen; using montauk::streq; using montauk::starts_with; using montauk::skip_spaces; -// ---- Minimal snprintf (no libc available) ---- - -using va_list = __builtin_va_list; -#define va_start __builtin_va_start -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg - -struct PfState { - char* buf; - int pos; - int max; -}; - -static void pf_putc(PfState* st, char c) { - if (st->pos < st->max) st->buf[st->pos] = c; - st->pos++; -} - -static void pf_putnum(PfState* st, unsigned long val, int base, int width, char pad, int neg) { - char tmp[24]; - int i = 0; - const char* digits = "0123456789abcdef"; - if (val == 0) { tmp[i++] = '0'; } - else { while (val > 0) { tmp[i++] = digits[val % base]; val /= base; } } - int total = (neg ? 1 : 0) + i; - if (neg && pad == '0') pf_putc(st, '-'); - for (int w = total; w < width; w++) pf_putc(st, pad); - if (neg && pad != '0') pf_putc(st, '-'); - while (i > 0) pf_putc(st, tmp[--i]); -} - -static int vsnprintf(char* buf, int size, const char* fmt, va_list ap) { - PfState st; - st.buf = buf; - st.pos = 0; - st.max = size > 0 ? size - 1 : 0; - while (*fmt) { - if (*fmt != '%') { pf_putc(&st, *fmt++); continue; } - fmt++; - char pad = ' '; - if (*fmt == '0') { pad = '0'; fmt++; } - int width = 0; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } - if (*fmt == 'l') fmt++; - switch (*fmt) { - case 'd': case 'i': { - long val = va_arg(ap, int); - int neg = 0; unsigned long uval; - if (val < 0) { neg = 1; uval = (unsigned long)(-val); } - else uval = (unsigned long)val; - pf_putnum(&st, uval, 10, width, pad, neg); - break; - } - case 'u': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 10, width, pad, 0); break; } - case 'x': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 16, width, pad, 0); break; } - case 's': { - const char* s = va_arg(ap, const char*); - if (!s) s = "(null)"; - int slen = 0; while (s[slen]) slen++; - for (int w = slen; w < width; w++) pf_putc(&st, ' '); - for (int j = 0; j < slen; j++) pf_putc(&st, s[j]); - break; - } - case 'c': { char c = (char)va_arg(ap, int); pf_putc(&st, c); break; } - case '%': pf_putc(&st, '%'); break; - default: pf_putc(&st, '%'); pf_putc(&st, *fmt); break; - } - if (*fmt) fmt++; - } - if (size > 0) { - if (st.pos < size) st.buf[st.pos] = '\0'; - else st.buf[size - 1] = '\0'; - } - return st.pos; -} - -static int snprintf(char* buf, int size, const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - int ret = vsnprintf(buf, size, fmt, ap); - va_end(ap); - return ret; -} - // ---- IP/port parsing ---- static bool parse_uint16(const char* s, uint16_t* out) { diff --git a/programs/src/init/main.cpp b/programs/src/init/main.cpp index add49f8..3d387ad 100644 --- a/programs/src/init/main.cpp +++ b/programs/src/init/main.cpp @@ -6,74 +6,7 @@ */ #include - -// ---- Minimal snprintf ---- - -using va_list = __builtin_va_list; -#define va_start __builtin_va_start -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg - -struct PfState { char* buf; int pos; int max; }; - -static void pf_putc(PfState* st, char c) { - if (st->pos < st->max) st->buf[st->pos] = c; - st->pos++; -} - -static void pf_putnum(PfState* st, unsigned long val, int base, int width, char pad, int neg) { - char tmp[24]; int i = 0; - const char* digits = "0123456789abcdef"; - if (val == 0) { tmp[i++] = '0'; } - else { while (val > 0) { tmp[i++] = digits[val % base]; val /= base; } } - int total = (neg ? 1 : 0) + i; - if (neg && pad == '0') pf_putc(st, '-'); - for (int w = total; w < width; w++) pf_putc(st, pad); - if (neg && pad != '0') pf_putc(st, '-'); - while (i > 0) pf_putc(st, tmp[--i]); -} - -static int vsnprintf(char* buf, int size, const char* fmt, va_list ap) { - PfState st; st.buf = buf; st.pos = 0; st.max = size > 0 ? size - 1 : 0; - while (*fmt) { - if (*fmt != '%') { pf_putc(&st, *fmt++); continue; } - fmt++; - char pad = ' '; - if (*fmt == '0') { pad = '0'; fmt++; } - int width = 0; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } - if (*fmt == 'l') fmt++; - switch (*fmt) { - case 'd': case 'i': { - long val = va_arg(ap, int); - int neg = 0; unsigned long uval; - if (val < 0) { neg = 1; uval = (unsigned long)(-val); } else uval = (unsigned long)val; - pf_putnum(&st, uval, 10, width, pad, neg); break; - } - case 'u': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 10, width, pad, 0); break; } - case 'x': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 16, width, pad, 0); break; } - case 's': { - const char* s = va_arg(ap, const char*); if (!s) s = "(null)"; - int slen = 0; while (s[slen]) slen++; - for (int w = slen; w < width; w++) pf_putc(&st, ' '); - for (int j = 0; j < slen; j++) pf_putc(&st, s[j]); - break; - } - case 'c': { char c = (char)va_arg(ap, int); pf_putc(&st, c); break; } - case '%': pf_putc(&st, '%'); break; - default: pf_putc(&st, '%'); pf_putc(&st, *fmt); break; - } - if (*fmt) fmt++; - } - if (size > 0) { if (st.pos < size) st.buf[st.pos] = '\0'; else st.buf[size - 1] = '\0'; } - return st.pos; -} - -static int snprintf(char* buf, int size, const char* fmt, ...) { - va_list ap; va_start(ap, fmt); - int ret = vsnprintf(buf, size, fmt, ap); - va_end(ap); return ret; -} +#include // ---- ANSI color codes ---- diff --git a/programs/src/installer/Makefile b/programs/src/installer/Makefile index 3dd00b3..82050a6 100644 --- a/programs/src/installer/Makefile +++ b/programs/src/installer/Makefile @@ -63,7 +63,7 @@ TARGET := $(BINDIR)/apps/installer/installer.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/installer $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/irc/main.cpp b/programs/src/irc/main.cpp index 844d2e0..585d1fc 100644 --- a/programs/src/irc/main.cpp +++ b/programs/src/irc/main.cpp @@ -7,6 +7,7 @@ #include #include +#include using montauk::slen; using montauk::streq; @@ -17,90 +18,6 @@ using montauk::strncpy; using montauk::memcpy; using montauk::memmove; -// ---- Minimal snprintf (no libc available) ---- - -using va_list = __builtin_va_list; -#define va_start __builtin_va_start -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg - -struct PfState { - char* buf; - int pos; - int max; -}; - -static void pf_putc(PfState* st, char c) { - if (st->pos < st->max) st->buf[st->pos] = c; - st->pos++; -} - -static void pf_putnum(PfState* st, unsigned long val, int base, int width, char pad, int neg) { - char tmp[24]; - int i = 0; - const char* digits = "0123456789abcdef"; - if (val == 0) { tmp[i++] = '0'; } - else { while (val > 0) { tmp[i++] = digits[val % base]; val /= base; } } - int total = (neg ? 1 : 0) + i; - if (neg && pad == '0') pf_putc(st, '-'); - for (int w = total; w < width; w++) pf_putc(st, pad); - if (neg && pad != '0') pf_putc(st, '-'); - while (i > 0) pf_putc(st, tmp[--i]); -} - -static int vsnprintf(char* buf, int size, const char* fmt, va_list ap) { - PfState st; - st.buf = buf; - st.pos = 0; - st.max = size > 0 ? size - 1 : 0; - while (*fmt) { - if (*fmt != '%') { pf_putc(&st, *fmt++); continue; } - fmt++; - char pad = ' '; - if (*fmt == '0') { pad = '0'; fmt++; } - int width = 0; - while (*fmt >= '0' && *fmt <= '9') { width = width * 10 + (*fmt - '0'); fmt++; } - if (*fmt == 'l') fmt++; - switch (*fmt) { - case 'd': case 'i': { - long val = va_arg(ap, int); - int neg = 0; unsigned long uval; - if (val < 0) { neg = 1; uval = (unsigned long)(-val); } - else uval = (unsigned long)val; - pf_putnum(&st, uval, 10, width, pad, neg); - break; - } - case 'u': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 10, width, pad, 0); break; } - case 'x': { unsigned val = va_arg(ap, unsigned); pf_putnum(&st, val, 16, width, pad, 0); break; } - case 's': { - const char* s = va_arg(ap, const char*); - if (!s) s = "(null)"; - int slen = 0; while (s[slen]) slen++; - for (int w = slen; w < width; w++) pf_putc(&st, ' '); - for (int j = 0; j < slen; j++) pf_putc(&st, s[j]); - break; - } - case 'c': { char c = (char)va_arg(ap, int); pf_putc(&st, c); break; } - case '%': pf_putc(&st, '%'); break; - default: pf_putc(&st, '%'); pf_putc(&st, *fmt); break; - } - if (*fmt) fmt++; - } - if (size > 0) { - if (st.pos < size) st.buf[st.pos] = '\0'; - else st.buf[size - 1] = '\0'; - } - return st.pos; -} - -static int snprintf(char* buf, int size, const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - int ret = vsnprintf(buf, size, fmt, ap); - va_end(ap); - return ret; -} - // Case-insensitive comparison for IRC commands static bool streqi(const char* a, const char* b) { while (*a && *b) { diff --git a/programs/src/klog/Makefile b/programs/src/klog/Makefile index 76ced1b..b0c2478 100644 --- a/programs/src/klog/Makefile +++ b/programs/src/klog/Makefile @@ -51,7 +51,7 @@ TARGET := $(BINDIR)/apps/klog/klog.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/klog $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/lua/Makefile b/programs/src/lua/Makefile index ca89025..1189f99 100644 --- a/programs/src/lua/Makefile +++ b/programs/src/lua/Makefile @@ -39,9 +39,13 @@ CFLAGS := \ -isystem ../../include/libc \ -isystem $(shell $(CC) -print-file-name=include) +# -u _pf_putfloat forces liblibc.a's optional printf_float.o member to be +# linked: Lua formats floats (%.14g in lobject.c, %f/%e/%g via string.format), +# and vsnprintf only references the float conversions weakly. LDFLAGS := \ -nostdlib \ -Wl,--gc-sections \ + -Wl,-u,_pf_putfloat \ -T ../../link.ld CORE_SRCS := \ diff --git a/programs/src/mandelbrot/Makefile b/programs/src/mandelbrot/Makefile index 6152939..4886b13 100644 --- a/programs/src/mandelbrot/Makefile +++ b/programs/src/mandelbrot/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/mandelbrot/mandelbrot.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/mandelbrot $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/music/Makefile b/programs/src/music/Makefile index d67f3fd..382faa1 100644 --- a/programs/src/music/Makefile +++ b/programs/src/music/Makefile @@ -63,7 +63,7 @@ TARGET := $(BINDIR)/apps/music/music.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/music $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/paint/Makefile b/programs/src/paint/Makefile index 426ba39..a8ef53a 100644 --- a/programs/src/paint/Makefile +++ b/programs/src/paint/Makefile @@ -62,7 +62,7 @@ TARGET := $(BINDIR)/apps/paint/paint.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/paint $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/pdfviewer/Makefile b/programs/src/pdfviewer/Makefile index d96d79e..1591b16 100644 --- a/programs/src/pdfviewer/Makefile +++ b/programs/src/pdfviewer/Makefile @@ -63,7 +63,7 @@ LOADER_LIB := $(LIBDIR)/libloader/liblibloader.a all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LOADER_LIB) +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LOADER_LIB) $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/pdfviewer $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LOADER_LIB) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/powermgr/Makefile b/programs/src/powermgr/Makefile index 814b5af..14c6e3d 100644 --- a/programs/src/powermgr/Makefile +++ b/programs/src/powermgr/Makefile @@ -51,7 +51,7 @@ TARGET := $(BINDIR)/apps/powermgr/powermgr.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/powermgr $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/printctl/Makefile b/programs/src/printctl/Makefile index 28f6302..33a1395 100644 --- a/programs/src/printctl/Makefile +++ b/programs/src/printctl/Makefile @@ -51,7 +51,7 @@ DEPS := $(OBJDIR)/main.d all: $(TARGET) -$(TARGET): $(OBJDIR)/main.o $(LINK_LD) Makefile +$(TARGET): $(OBJDIR)/main.o $(LINK_LD) Makefile $(LIBC_LIB)/liblibc.a mkdir -p $(BINDIR)/os $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJDIR)/main.o $(TLS_LIB)/libtls.a $(BEARSSL)/libbearssl.a $(LIBC_LIB)/liblibc.a -o $@ diff --git a/programs/src/procmgr/Makefile b/programs/src/procmgr/Makefile index 820be26..96cf3fd 100644 --- a/programs/src/procmgr/Makefile +++ b/programs/src/procmgr/Makefile @@ -51,7 +51,7 @@ TARGET := $(BINDIR)/apps/procmgr/procmgr.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/procmgr $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/rpgdemo/Makefile b/programs/src/rpgdemo/Makefile index cf98789..e89f551 100644 --- a/programs/src/rpgdemo/Makefile +++ b/programs/src/rpgdemo/Makefile @@ -69,7 +69,7 @@ APP_DIR := $(BINDIR)/apps/rpgdemo all: $(TARGET) assets -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(APP_DIR) $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/spreadsheet/Makefile b/programs/src/spreadsheet/Makefile index 8a9469b..4445f13 100644 --- a/programs/src/spreadsheet/Makefile +++ b/programs/src/spreadsheet/Makefile @@ -63,7 +63,7 @@ LOADER_LIB := $(LIBDIR)/libloader/liblibloader.a all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LOADER_LIB) +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LOADER_LIB) $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/spreadsheet $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LOADER_LIB) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/tcc/Makefile b/programs/src/tcc/Makefile index 017da8e..40298c7 100644 --- a/programs/src/tcc/Makefile +++ b/programs/src/tcc/Makefile @@ -53,9 +53,13 @@ CFLAGS := \ -isystem $(PROG_INC)/libc \ -isystem $(shell $(CC) -print-file-name=include) +# -u _pf_putfloat forces liblibc.a's optional printf_float.o member to be +# linked: tcc formats floats when dumping constants (tccpp.c), and vsnprintf +# only references the float conversions weakly. LDFLAGS := \ -nostdlib \ -Wl,--gc-sections \ + -Wl,-u,_pf_putfloat \ -T ../../link.ld # ---- Sources ---- @@ -110,7 +114,7 @@ $(OBJDIR)/%.o: %.S Makefile @mkdir -p $(OBJDIR) $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ -$(SHARED_CRT_DIR)/liblibc.a $(SHARED_CRT_DIR)/crt1.o $(SHARED_CRT_DIR)/crti.o $(SHARED_CRT_DIR)/crtn.o: +$(SHARED_CRT_DIR)/liblibc.a $(SHARED_CRT_DIR)/liblibc-full.a $(SHARED_CRT_DIR)/crt1.o $(SHARED_CRT_DIR)/crti.o $(SHARED_CRT_DIR)/crtn.o: $(MAKE) -C $(SHARED_CRT_DIR) $(CRT_DIR)/crt1.o: $(SHARED_CRT_DIR)/crt1.o @@ -125,7 +129,10 @@ $(CRT_DIR)/crtn.o: $(SHARED_CRT_DIR)/crtn.o @mkdir -p $(CRT_DIR) cp $< $@ -$(CRT_DIR)/libc.a: $(LIBDIR)/libc/liblibc.a +# liblibc-full.a, not liblibc.a: programs compiled on-OS with tcc cannot pass +# -u _pf_putfloat, so this sysroot ships the variant with the printf float path +# already bound in. See lib/libc/Makefile. +$(CRT_DIR)/libc.a: $(LIBDIR)/libc/liblibc-full.a @mkdir -p $(CRT_DIR) cp $< $@ diff --git a/programs/src/terminal/Makefile b/programs/src/terminal/Makefile index 9472b40..703cb13 100644 --- a/programs/src/terminal/Makefile +++ b/programs/src/terminal/Makefile @@ -51,7 +51,7 @@ TARGET := $(BINDIR)/apps/terminal/terminal.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/terminal $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/texteditor/Makefile b/programs/src/texteditor/Makefile index bcd5832..64ef351 100644 --- a/programs/src/texteditor/Makefile +++ b/programs/src/texteditor/Makefile @@ -63,7 +63,7 @@ LOADER_LIB := $(LIBDIR)/libloader/liblibloader.a all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LOADER_LIB) +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LOADER_LIB) $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/texteditor $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LOADER_LIB) $(LIBDIR)/libc/liblibc.a -o $@ diff --git a/programs/src/video/Makefile b/programs/src/video/Makefile index f6c963b..eefab16 100644 --- a/programs/src/video/Makefile +++ b/programs/src/video/Makefile @@ -51,7 +51,7 @@ TARGET := $(BINDIR)/apps/video/video.elf all: $(TARGET) -$(TARGET): $(OBJS) $(LINK_LD) Makefile +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBDIR)/libc/liblibc.a mkdir -p $(BINDIR)/apps/video $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBDIR)/libc/liblibc.a -o $@