diff --git a/Makefile b/Makefile index 6ae547ffbd8..96315cb03b6 100644 --- a/Makefile +++ b/Makefile @@ -216,30 +216,25 @@ tags: TAGS HTAGS o/$(MODE)/.x: @mkdir -p $(@D) && touch $@ -ifneq ($(findstring 4.,,$(MAKE_VERSION)),$(MAKE_VERSION)) o/$(MODE)/srcs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) - $(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x))) -o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) - $(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x))) -o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) - $(file >$@) $(foreach x,$(INCS) $(INCS),$(file >>$@,$(x))) -else -o/$(MODE)/srcs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) - $(MAKE) MODE=rel -j8 -pn bopit 2>/dev/null | sed -ne '/^SRCS/ {s/.*:= //;s/ */\n/g;p;q}' >$@ + $(file >$@,$(SRCS)) o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) - $(MAKE) MODE=rel -j8 -pn bopit 2>/dev/null | sed -ne '/^HDRS/ {s/.*:= //;s/ */\n/g;p;q}' >$@ + $(file >$@,$(HDRS) $(INCS)) o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) - $(MAKE) MODE=rel -j8 -pn bopit 2>/dev/null | sed -ne '/^INCS/ {s/.*:= //;s/ */\n/g;p;q}' >$@ -endif - + $(file >$@,$(INCS)) o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS) - @$(COMPILE) -AMKDEPS $(MKDEPS) -o $@ -r o/$(MODE)/ o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt + @$(COMPILE) -AMKDEPS $(MKDEPS) -o $@ -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt -TAGS: o/$(MODE)/srcs.txt $(SRCS) +o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x)))) + $(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x))) +o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) + $(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x))) + +TAGS: o/$(MODE)/srcs-old.txt $(SRCS) @rm -f $@ @$(COMPILE) -ATAGS -T$@ $(TAGS) $(TAGSFLAGS) -L $< -o $@ -HTAGS: o/$(MODE)/hdrs.txt $(HDRS) +HTAGS: o/$(MODE)/hdrs-old.txt $(HDRS) @rm -f $@ @$(COMPILE) -ATAGS -T$@ build/htags -L $< -o $@ @@ -326,14 +321,15 @@ COSMOPOLITAN_HEADERS = \ THIRD_PARTY_ZLIB \ THIRD_PARTY_REGEX -o/$(MODE)/cosmopolitan.a.txt: - printf "%s\n" $(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x))))) -o/$(MODE)/cosmopolitan.a: $(filter-out o/libc/stubs/exit11.o,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))) +o/$(MODE)/cosmopolitan.a: \ + $(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS)) + o/cosmopolitan.h: \ o/$(MODE)/tool/build/rollup.com \ libc/integral/normalize.inc \ $(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS)) - @$(COMPILE) -AROLLUP -T$@ $^ >$@ + $(file >$@.args,libc/integral/normalize.inc $(foreach x,$(COSMOPOLITAN_HEADERS),$($(x)_HDRS))) + @$(COMPILE) -AROLLUP -T$@ o/$(MODE)/tool/build/rollup.com @$@.args >$@ o/cosmopolitan.html: \ o/$(MODE)/third_party/chibicc/chibicc.com.dbg \ diff --git a/README.md b/README.md index a3f6f7654e1..9a3a231a242 100644 --- a/README.md +++ b/README.md @@ -92,4 +92,4 @@ find o -name \*.com | xargs ls -rShal | less | FreeBSD | 12 | 2018 | | OpenBSD | 6.4 | 2018 | | NetBSD | 9.1 | 2020 | -| GNU Make | 3.80 | 2010 | +| GNU Make | 4.0 | 2015 | diff --git a/ape/ape.lds b/ape/ape.lds index 3c2925842ec..b1ffd1383e5 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -485,10 +485,10 @@ HIDDEN(ape_ram_align = PAGESIZE); HIDDEN(ape_ram_rva = RVA(ape_ram_vaddr)); HIDDEN(ape_stack_offset = ape_ram_offset + ape_ram_filesz); -HIDDEN(ape_stack_vaddr = 0x700000000000 - STACKSIZE); +HIDDEN(ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000 - STACKSIZE); HIDDEN(ape_stack_paddr = ape_ram_paddr + ape_ram_filesz); HIDDEN(ape_stack_filesz = 0); -HIDDEN(ape_stack_memsz = STACKSIZE); +HIDDEN(ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : STACKSIZE); HIDDEN(ape_stack_align = 16); HIDDEN(ape_note_offset = ape_rom_offset + (ape_note - ape_rom_vaddr)); diff --git a/ape/config.h b/ape/config.h new file mode 100644 index 00000000000..01879423c59 --- /dev/null +++ b/ape/config.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_APE_CONFIG_H_ +#define COSMOPOLITAN_APE_CONFIG_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +#define STATIC_SYMBOL(NAME, VALUE) \ + asm(".equ\t" NAME "," VALUE "\n\t" \ + ".globl\t" NAME) + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_APE_CONFIG_H_ */ diff --git a/ape/loader.c b/ape/loader.c index 783bec71571..c79b0175708 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -68,59 +68,59 @@ static wontreturn void Exit(int os, long rc) { } static void Close(int os, long fd) { - long ax; + long ax, di; asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_close), "D"(fd) + : "=a"(ax), "=D"(di) + : "0"(__NR_close), "1"(fd) : "rcx", "rdx", "rsi", "r8", "r9", "r10", "r11", "memory", "cc"); } static long Read(int os, long fd, void *data, unsigned long size) { bool cf; - long ax; + long ax, di, si, dx; asm volatile("clc\n\t" "syscall" - : "=@ccc"(cf), "=a"(ax) - : "1"(__NR_read), "D"(fd), "S"(data), "d"(size) + : "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "1"(__NR_read), "2"(fd), "3"(data), "4"(size) : "rcx", "r8", "r9", "r10", "r11", "memory"); if (cf) ax = -ax; return ax; } static void Write(int os, long fd, const void *data, unsigned long size) { - long ax; + long ax, di, si, dx; asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_write), "D"(fd), "S"(data), "d"(size) + : "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "0"(__NR_write), "1"(fd), "2"(data), "3"(size) : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } static long Fstat(int os, long fd, union metastat *st) { - long ax; + long ax, di, si; asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_fstat), "D"(fd), "S"(st) - : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory"); + : "=a"(ax), "=D"(di), "=S"(si) + : "0"(__NR_fstat), "1"(fd), "2"(st) + : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); return ax; } static void Msyscall(int os, long p, long n) { - long ax; + long ax, di, si; if (os == OPENBSD) { asm volatile("syscall" - : "=a"(ax) - : "0"(37), "D"(p), "S"(n) + : "=a"(ax), "=D"(di), "=S"(si) + : "0"(37), "1"(p), "2"(n) : "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc"); } } static long Open(int os, const char *path, long flags, long mode) { bool cf; - long ax; + long ax, di, si, dx; asm volatile("clc\n\t" "syscall" - : "=@ccc"(cf), "=a"(ax) - : "1"(__NR_open), "D"(path), "S"(flags), "d"(mode) + : "=@ccc"(cf), "=a"(ax), "=D"(di), "=S"(si), "=d"(dx) + : "1"(__NR_open), "2"(path), "3"(flags), "4"(mode) : "rcx", "r8", "r9", "r10", "r11", "memory"); if (cf) ax = -ax; return ax; diff --git a/build/bootstrap/ar.com b/build/bootstrap/ar.com index fa831734892..7e77313557e 100755 Binary files a/build/bootstrap/ar.com and b/build/bootstrap/ar.com differ diff --git a/build/bootstrap/mkdeps.com b/build/bootstrap/mkdeps.com index 0aedeb92276..92a6b88a722 100755 Binary files a/build/bootstrap/mkdeps.com and b/build/bootstrap/mkdeps.com differ diff --git a/build/config.mk b/build/config.mk index c037be5112a..29f70628d0b 100644 --- a/build/config.mk +++ b/build/config.mk @@ -41,6 +41,7 @@ CONFIG_CPPFLAGS += \ CONFIG_CCFLAGS += \ $(BACKTRACES) \ + $(FTRACE) \ -O3 TARGET_ARCH ?= \ @@ -122,7 +123,8 @@ CONFIG_CPPFLAGS += \ CONFIG_CCFLAGS += \ $(BACKTRACES) \ $(FTRACE) \ - -O2 + -O2 \ + -fno-inline CONFIG_COPTS += \ -fsanitize=address @@ -159,7 +161,6 @@ CONFIG_CCFLAGS += \ -fno-align-labels \ -fno-align-loops \ -fschedule-insns2 \ - -fomit-frame-pointer \ -momit-leaf-frame-pointer \ -foptimize-sibling-calls TARGET_ARCH ?= \ diff --git a/build/definitions.mk b/build/definitions.mk index 65585149b91..647da4a0dea 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -88,6 +88,10 @@ export TMPDIR FTRACE = \ -pg +BACKTRACES = \ + -fno-optimize-sibling-calls \ + -mno-omit-leaf-frame-pointer + SANITIZER = \ -fsanitize=address @@ -126,8 +130,7 @@ DEFAULT_COPTS = \ -fstrict-aliasing \ -fstrict-overflow \ -fno-omit-frame-pointer \ - -fno-semantic-interposition \ - -mno-omit-leaf-frame-pointer + -fno-semantic-interposition MATHEMATICAL = \ -O3 \ diff --git a/build/rules.mk b/build/rules.mk index f6154322c23..554d958da73 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -16,7 +16,6 @@ MAKEFLAGS += --no-builtin-rules -o/%.a: ; @$(COMPILE) -AARCHIVE -T$@ $(AR) $(ARFLAGS) $@ $^ o/%.o: %.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $< o/%.o: o/%.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $< o/%.s: %.S ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) $< @@ -36,7 +35,6 @@ o/%.h.okk: %.h ; @$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ o/%.greg.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $< o/%.zip.o: o/% ; @$(COMPILE) -AZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $< -o/$(MODE)/%.a: ; @$(COMPILE) -AARCHIVE -T$@ $(AR) $(ARFLAGS) $@ $^ o/$(MODE)/%: o/$(MODE)/%.dbg ; @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@ o/$(MODE)/%.o: %.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $< o/$(MODE)/%.o: o/$(MODE)/%.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $< @@ -82,6 +80,10 @@ o/$(MODE)/%-gcc.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S o/$(MODE)/%-clang.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $< o/$(MODE)/%-clang.asm: CC = $(CLANG) +o/%.a: + $(file >$@.args,$^) + @$(COMPILE) -AARCHIVE -T$@ $(AR) $(ARFLAGS) $@ @$@.args + o/$(MODE)/%.o: %.py o/$(MODE)/third_party/python/pyobj.com @$(COMPILE) -APYOBJ o/$(MODE)/third_party/python/pyobj.com $(PYFLAGS) -o $@ $< diff --git a/dsp/tty/rgb2ansi.c b/dsp/tty/rgb2ansi.c index 9ffdb73e6d9..cbf214b64f2 100644 --- a/dsp/tty/rgb2ansi.c +++ b/dsp/tty/rgb2ansi.c @@ -47,7 +47,8 @@ __m128 tty2rgbf_(struct TtyRgb rgbxt) { static int rgb2xterm256_(int r, int g, int b) { int cerr, gerr, ir, ig, ib, gray, grai, cr, cg, cb, gv; - gray = round(r * .299 + g * .587 + b * .114); + gray = round(871024 / 4096299. * r + 8788810 / 12288897. * g + + 887015 / 12288897. * b); grai = gray > 238 ? 23 : (gray - 3) / 10; ir = r < 48 ? 0 : r < 115 ? 1 : (r - 35) / 40; ig = g < 48 ? 0 : g < 115 ? 1 : (g - 35) / 40; diff --git a/examples/ctrlc.c b/examples/ctrlc.c index 6073ad3f39b..d24fcc91426 100644 --- a/examples/ctrlc.c +++ b/examples/ctrlc.c @@ -8,6 +8,7 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/errno.h" #include "libc/log/check.h" #include "libc/log/color.internal.h" diff --git a/examples/getrandom.c b/examples/getrandom.c index 00371b386d1..834fd39c7fa 100644 --- a/examples/getrandom.c +++ b/examples/getrandom.c @@ -9,6 +9,7 @@ #endif #include "libc/bits/bits.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/log/check.h" diff --git a/examples/rusage.c b/examples/rusage.c index 07ab2ec7d51..2112c184aeb 100644 --- a/examples/rusage.c +++ b/examples/rusage.c @@ -10,6 +10,7 @@ #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/calls/struct/rusage.h" +#include "libc/calls/struct/sigaction.h" #include "libc/errno.h" #include "libc/log/check.h" #include "libc/log/log.h" diff --git a/examples/stackoverflow.c b/examples/stackoverflow.c new file mode 100644 index 00000000000..30c6215020d --- /dev/null +++ b/examples/stackoverflow.c @@ -0,0 +1,77 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" +#include "libc/limits.h" +#include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/runtime/stack.h" +#include "libc/stdio/stdio.h" +#include "libc/sysv/consts/prot.h" + +/** + * @fileoverview Stack Overflow Demo + */ + +#define N INT_MAX + +STATIC_STACK_SIZE(FRAMESIZE); + +int A(int f(), int n) { + if (n < N) { + return f(f, n + 1) - 1; + } else { + return N; + } +} + +int (*Ap)(int (*)(), int) = A; + +int main(int argc, char *argv[]) { + ShowCrashReports(); + return !!Ap(Ap, 0); +} + +/* +error: Uncaught SIGSEGV (Stack Overflow) on rhel5 pid 368 + ./o//examples/stackoverflow.com + EUNKNOWN[No error information][0] + Linux rhel5 2.6.18-8.el5 #1 SMP Thu Mar 15 19:46:53 EDT 2007 + +0x0000000000406896: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +0x0000000000406898: A at examples/stackoverflow.c:24 +etc. etc. + +RAX 0000000000000000 RBX 0000000000000001 RDI 000000000040687e ST(0) 0.0 +RCX 0000000000417125 RDX 000000000041cd70 RSI 0000000000000efe ST(1) 0.0 +RBP 00006ffffffe1000 RSP 00006ffffffe1000 RIP 0000000000406897 ST(2) 0.0 + R8 0000000000000000 R9 0000000000000022 R10 0000000000000008 ST(3) 0.0 +R11 0000000000000293 R12 0000000000000001 R13 00007ffc70b4fc48 ST(4) 0.0 +R14 00007ffc70b4fc58 R15 00007ffc70b4fd18 VF IF + +XMM0 00000000000000000000000000000000 XMM8 00000000000000000000000000000000 +XMM1 ffffffffffffeb030000000000000000 XMM9 00000000000000000000000000000000 +XMM2 0000000000000000ffffffffffffffff XMM10 00000000000000000000000000000000 +XMM3 00000000000000000000000000000000 XMM11 00000000000000000000000000000000 +XMM4 00000000000000000000000000000000 XMM12 00000000000000000000000000000000 +XMM5 00000000000000000000000000000000 XMM13 00000000000000000000000000000000 +XMM6 00000000000000000000000000000000 XMM14 00000000000000000000000000000000 +XMM7 00000000000000000000000000000000 XMM15 00000000000000000000000000000000 + +100080000000-100080030000 rw-pa-- 3x automap +6ffffffe0000-6fffffff0000 rw-paSF 1x stack +# 4 frames mapped w/ 0 frames gapped +*/ diff --git a/examples/unbourne.c b/examples/unbourne.c index 6fe7b163c85..5582976674f 100644 --- a/examples/unbourne.c +++ b/examples/unbourne.c @@ -602,30 +602,16 @@ * shell. If SHELL is defined, we try to map the standard UNIX library * routines to ash routines using defines. */ -/* #undef stdout /\* TODO(jart): XXX *\/ */ -/* #undef stderr */ -/* #undef putc */ -/* #undef putchar */ -#undef fileno -/* #define stdout out1 */ -/* #define stderr out2 */ -#undef printf -#define printf out1fmt -/* #define putc(c, file) outc(c, file) */ -/* #define putchar(c) out1c(c) */ -#define FILE struct output -#define fileno(f) ((f)->fd) -/* #define ferror outerr */ +#define Printf out1fmt #define INITARGS(argv) #define setprogname(s) #define getprogname() commandname #define setlocate(l, s) 0 -#define equal(s1, s2) (strcmp(s1, s2) == 0) -/* #define getenv(p) bltinlookup((p), 0) */ -#define isodigit(c) ((c) >= '0' && (c) <= '7') -#define octtobin(c) ((c) - '0') -#define scopy(s1, s2) ((void)strcpy(s2, s1)) +#define equal(s1, s2) (!strcmp(s1, s2)) +#define isodigit(c) ((c) >= '0' && (c) <= '7') +#define octtobin(c) ((c) - '0') +#define scopy(s1, s2) ((void)strcpy(s2, s1)) #define TRACE(param) /* #define TRACE(param) \ */ @@ -9483,13 +9469,13 @@ static void sigblockall(sigset_t *oldmask) { { \ switch ((char *)param - (char *)array) { \ default: \ - (void)printf(f, array[0], array[1], func); \ + (void)Printf(f, array[0], array[1], func); \ break; \ case sizeof(*param): \ - (void)printf(f, array[0], func); \ + (void)Printf(f, array[0], func); \ break; \ case 0: \ - (void)printf(f, func); \ + (void)Printf(f, func); \ break; \ } \ } @@ -10118,7 +10104,7 @@ static int timescmd() { struct tms buf; long int clk_tck = sysconf(_SC_CLK_TCK); times(&buf); - printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n", (int)(buf.tms_utime / clk_tck / 60), + Printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n", (int)(buf.tms_utime / clk_tck / 60), ((double)buf.tms_utime) / clk_tck, (int)(buf.tms_stime / clk_tck / 60), ((double)buf.tms_stime) / clk_tck, (int)(buf.tms_cutime / clk_tck / 60), ((double)buf.tms_cutime) / clk_tck, (int)(buf.tms_cstime / clk_tck / 60), diff --git a/libc/calls/calls.h b/libc/calls/calls.h index c4303c12a91..d4121ae94b3 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -5,13 +5,13 @@ #include "libc/calls/struct/rlimit.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigval.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/sysinfo.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/tms.h" #include "libc/calls/struct/utsname.h" -#include "libc/calls/typedef/sighandler_t.h" #include "libc/dce.h" #include "libc/fmt/pflink.h" #include "libc/sysv/consts/s.h" @@ -178,7 +178,6 @@ int setreuid(uint32_t, uint32_t); int setrlimit(int, const struct rlimit *); int setsid(void); int setuid(uint32_t); -int sigaction(int, const struct sigaction *, struct sigaction *); int sigignore(int); int siginterrupt(int, int); int sigprocmask(int, const struct sigset *, struct sigset *); @@ -206,7 +205,6 @@ intptr_t syscall(int, ...); long ptrace(int, int, void *, void *); long telldir(DIR *); long times(struct tms *); -sighandler_t signal(int, sighandler_t); size_t GetFileSize(const char *); size_t getfiledescriptorsize(int); ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t); @@ -247,51 +245,6 @@ int vdprintf(int, const char *, va_list) paramsnonnull(); ╚────────────────────────────────────────────────────────────────────────────│*/ #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -void _init_onntconsoleevent(void); -void _init_wincrash(void); - -#ifndef __SIGACTION_YOINK -#define __SIGACTION_YOINK(SIG) \ - do { \ - if (SupportsWindows()) { \ - if (__builtin_constant_p(SIG)) { \ - switch (SIG) { \ - case SIGINT: \ - case SIGQUIT: \ - case SIGHUP: \ - case SIGTERM: \ - YOINK(_init_onntconsoleevent); \ - break; \ - case SIGTRAP: \ - case SIGILL: \ - case SIGSEGV: \ - case SIGABRT: \ - case SIGFPE: \ - YOINK(_init_wincrash); \ - break; \ - default: \ - break; \ - } \ - } else { \ - YOINK(_init_onntconsoleevent); \ - YOINK(_init_wincrash); \ - } \ - } \ - } while (0) -#endif - -#define sigaction(SIG, ACT, OLD) \ - ({ \ - __SIGACTION_YOINK(SIG); \ - sigaction(SIG, (ACT), OLD); \ - }) - -#define signal(SIG, HAND) \ - ({ \ - __SIGACTION_YOINK(SIG); \ - signal(SIG, HAND); \ - }) - #define dprintf(FD, FMT, ...) (dprintf)(FD, PFLINK(FMT), ##__VA_ARGS__) #define vdprintf(FD, FMT, VA) (vdprintf)(FD, PFLINK(FMT), VA) diff --git a/libc/calls/commandv.c b/libc/calls/commandv.c index 25818504fa4..ced97a9b1dc 100644 --- a/libc/calls/commandv.c +++ b/libc/calls/commandv.c @@ -18,39 +18,55 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" +#include "libc/log/libfatal.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" -static noasan bool AccessCommand(char path[hasatleast PATH_MAX], - const char *name, size_t namelen, - size_t pathlen) { - if (pathlen + 1 + namelen + 1 + 4 + 1 > PATH_MAX) return -1; +static noasan bool EndsWithIgnoreCase(const char *p, size_t n, const char *s) { + size_t i, m; + m = __strlen(s); + if (n >= m) { + for (i = n - m; i < n; ++i) { + if (kToLower[p[i] & 255] != (*s++ & 255)) { + return false; + } + } + return true; + } else { + return false; + } +} + +static noasan bool AccessCommand(const char *name, + char path[hasatleast PATH_MAX], size_t namelen, + const char *suffix, size_t pathlen) { + size_t suffixlen; + suffixlen = __strlen(suffix); + if (pathlen + 1 + namelen + suffixlen + 1 > PATH_MAX) return -1; if (pathlen && (path[pathlen - 1] != '/' && path[pathlen - 1] != '\\')) { - path[pathlen] = !IsWindows() ? '/' - : memchr(path, '\\', pathlen) ? '\\' - : '/'; + path[pathlen] = !IsWindows() ? '/' + : __memchr(path, '\\', pathlen) ? '\\' + : '/'; pathlen++; } - memcpy(path + pathlen, name, namelen + 1); - if (isexecutable(path)) return true; - memcpy(path + pathlen + namelen, ".com", 5); - if (isexecutable(path)) return true; - memcpy(path + pathlen + namelen, ".exe", 5); - if (isexecutable(path)) return true; - return false; + __repmovsb(path + pathlen, name, namelen); + __repmovsb(path + pathlen + namelen, suffix, suffixlen + 1); + return isexecutable(path); } -static noasan bool SearchPath(char path[hasatleast PATH_MAX], const char *name, - size_t namelen) { +static noasan bool SearchPath(const char *name, char path[hasatleast PATH_MAX], + size_t namelen, const char *suffix) { size_t i; const char *p; p = firstnonnull(emptytonull(getenv("PATH")), "/bin:/usr/local/bin:/usr/bin"); for (;;) { for (i = 0; p[i] && p[i] != ':' && p[i] != ';'; ++i) { - if (i < PATH_MAX) path[i] = p[i]; + if (i < PATH_MAX) { + path[i] = p[i]; + } } - if (AccessCommand(path, name, namelen, i)) { + if (AccessCommand(name, path, namelen, suffix, i)) { return true; } if (p[i] == ':' || p[i] == ';') { @@ -62,6 +78,23 @@ static noasan bool SearchPath(char path[hasatleast PATH_MAX], const char *name, return false; } +static noasan bool FindCommand(const char *name, + char pathbuf[hasatleast PATH_MAX], + size_t namelen, const char *suffix) { + if (memchr(name, '/', namelen) || memchr(name, '\\', namelen)) { + pathbuf[0] = 0; + return AccessCommand(name, pathbuf, namelen, suffix, 0); + } + return ((IsWindows() && + (AccessCommand(name, pathbuf, namelen, suffix, + stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) || + AccessCommand(name, pathbuf, namelen, suffix, + stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf) || + AccessCommand(name, pathbuf, namelen, suffix, + stpcpy(pathbuf, ".") - pathbuf))) || + SearchPath(name, pathbuf, namelen, suffix)); +} + /** * Resolves full pathname of executable. * @@ -72,40 +105,28 @@ static noasan bool SearchPath(char path[hasatleast PATH_MAX], const char *name, * @vforksafe */ noasan char *commandv(const char *name, char pathbuf[hasatleast PATH_MAX]) { - char *p; + int olderr; size_t namelen; - int rc, olderr; if (!name) { efault(); - return NULL; + return 0; } - if (!(namelen = strlen(name))) { + if (!(namelen = __strlen(name))) { enoent(); - return NULL; + return 0; } if (namelen + 1 > PATH_MAX) { enametoolong(); - return NULL; - } - if (strchr(name, '/') || strchr(name, '\\')) { - if (AccessCommand(strcpy(pathbuf, ""), name, namelen, 0)) { - return pathbuf; - } else { - return NULL; - } + return 0; } - olderr = errno; - if ((IsWindows() && - (AccessCommand(pathbuf, name, namelen, - stpcpy(pathbuf, kNtSystemDirectory) - pathbuf) || - AccessCommand(pathbuf, name, namelen, - stpcpy(pathbuf, kNtWindowsDirectory) - pathbuf) || - AccessCommand(pathbuf, name, namelen, - stpcpy(pathbuf, ".") - pathbuf))) || - SearchPath(pathbuf, name, namelen)) { - errno = olderr; + if (FindCommand(name, pathbuf, namelen, "") || + (!EndsWithIgnoreCase(name, namelen, ".exe") && + !EndsWithIgnoreCase(name, namelen, ".com") && + !EndsWithIgnoreCase(name, namelen, ".com.dbg") && + (FindCommand(name, pathbuf, namelen, ".com") || + FindCommand(name, pathbuf, namelen, ".exe")))) { return pathbuf; } else { - return NULL; + return 0; } } diff --git a/libc/calls/ensurefds.c b/libc/calls/ensurefds.c index 2e7b807c804..e036b878465 100644 --- a/libc/calls/ensurefds.c +++ b/libc/calls/ensurefds.c @@ -24,6 +24,10 @@ #include "libc/str/str.h" #include "libc/sysv/errfuns.h" +static void __ensurefds_destroy(void) { + weaken(free)(g_fds.p); +} + int __ensurefds(int fd) { size_t n1, n2; struct Fd *p1, *p2; @@ -36,9 +40,15 @@ int __ensurefds(int fd) { if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) { memcpy(p2, p1, n1 * sizeof(*p1)); bzero(p2 + n1, (n2 - n1) * sizeof(*p1)); - if (p1 != g_fds.__init_p && weaken(free)) weaken(free)(p1); if (cmpxchg(&g_fds.p, p1, p2)) { g_fds.n = n2; + if (weaken(free)) { + if (p1 == g_fds.__init_p) { + atexit(__ensurefds_destroy); + } else { + weaken(free)(p1); + } + } return fd; } else if (weaken(free)) { weaken(free)(p2); diff --git a/libc/calls/getcwd.c b/libc/calls/getcwd.c index 2787b875071..536d8df798a 100644 --- a/libc/calls/getcwd.c +++ b/libc/calls/getcwd.c @@ -19,6 +19,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" #include "libc/mem/mem.h" #include "libc/str/str.h" @@ -42,15 +43,18 @@ char *getcwd(char *buf, size_t size) { if (buf) { p = buf; if (!size) { + SYSDEBUG("getcwd(%p, %x) EINVAL", buf, size); einval(); return 0; } } else if (weaken(malloc)) { if (!size) size = PATH_MAX + 1; if (!(p = weaken(malloc)(size))) { + SYSDEBUG("getcwd(%p, %x) ENOMEM", buf, size); return 0; } } else { + SYSDEBUG("getcwd() EINVAL needs buf≠0 or STATIC_YOINK(\"malloc\")"); einval(); return 0; } @@ -81,5 +85,6 @@ char *getcwd(char *buf, size_t size) { } } } + SYSDEBUG("getcwd(%p, %x) -> %s", buf, size, r); return r; } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 0b128b3034d..c5de5d92d0b 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -12,6 +12,7 @@ #include "libc/calls/struct/stat.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timeval.h" +#include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/limits.h" #include "libc/macros.internal.h" @@ -115,6 +116,7 @@ i32 __sys_fcntl(i32, i32, ...) hidden; i32 __sys_fstat(i32, void *) hidden; i32 __sys_fstatat(i32, const char *, void *, i32) hidden; i32 __sys_getrusage(i32, struct rusage *) hidden; +i32 __sys_munmap(void *, u64) hidden; i32 __sys_openat(i32, const char *, i32, u32) hidden; i32 __sys_pipe2(i32[hasatleast 2], u32) hidden; i32 __sys_utimensat(i32, const char *, const struct timespec *, i32) hidden; @@ -177,6 +179,7 @@ i32 sys_setresuid(uint32_t, uint32_t, uint32_t) hidden; i32 sys_setrlimit(i32, const struct rlimit *) hidden; i32 sys_setsid(void) hidden; i32 sys_sigaction(i32, const void *, void *, i64, i64) hidden; +i32 sys_sigaltstack(const void *, void *) hidden; i32 sys_sigprocmask(i32, const sigset *, sigset *, u64) hidden; i32 sys_sigqueue(i32, i32, const union sigval) hidden; i32 sys_sigqueueinfo(i32, const siginfo_t *) hidden; diff --git a/libc/calls/ioctl_siocgifconf-nt.c b/libc/calls/ioctl_siocgifconf-nt.c index 3a1e4cede96..1c62ce33b63 100644 --- a/libc/calls/ioctl_siocgifconf-nt.c +++ b/libc/calls/ioctl_siocgifconf-nt.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" @@ -25,6 +26,7 @@ #include "libc/nt/runtime.h" #include "libc/nt/struct/ipadapteraddresses.h" #include "libc/nt/winsock.h" +#include "libc/runtime/runtime.h" #include "libc/sock/internal.h" #include "libc/sock/sock.h" #include "libc/str/str.h" @@ -239,6 +241,7 @@ struct HostAdapterInfoNode *appendHostInfo( /* Returns -1 in case of failure */ static int createHostInfo(struct NtIpAdapterAddresses *firstAdapter) { + static bool once; struct NtIpAdapterAddresses *aa; struct NtIpAdapterUnicastAddress *ua; struct NtIpAdapterPrefix *ap; @@ -270,7 +273,12 @@ static int createHostInfo(struct NtIpAdapterAddresses *firstAdapter) { (ua != NULL) && (count < MAX_UNICAST_ADDR); ++count) { node = appendHostInfo(node, baseName, aa, &ua, &ap, count); if (!node) goto err; - if (!__hostInfo) __hostInfo = node; + if (!__hostInfo) { + __hostInfo = node; + if (cmpxchg(&once, false, true)) { + atexit(freeHostInfo); + } + } } /* Note: do we need to process the remaining adapter prefix? diff --git a/libc/calls/mkntpath.c b/libc/calls/mkntpath.c index 5410685bd12..0f60e65ff52 100644 --- a/libc/calls/mkntpath.c +++ b/libc/calls/mkntpath.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/ntmagicpaths.internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/nt/systeminfo.h" #include "libc/str/oldutf16.internal.h" #include "libc/str/str.h" @@ -94,7 +95,10 @@ textwindows int __mkntpath2(const char *path, m = 0; } n = tprecode8to16(p, z, q).ax; - if (n == z - 1) return enametoolong(); + if (n == z - 1) { + SYSDEBUG("path too long for windows: %s", path); + return enametoolong(); + } for (i = 0; i < n; ++i) { if (p[i] == '/') { p[i] = '\\'; diff --git a/libc/calls/mkntpathat.c b/libc/calls/mkntpathat.c index 0925c2d186b..a2f4e2cef42 100644 --- a/libc/calls/mkntpathat.c +++ b/libc/calls/mkntpathat.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/macros.internal.h" #include "libc/nt/files.h" #include "libc/str/str.h" @@ -33,7 +34,10 @@ int __mkntpathat(int dirfd, const char *path, int flags, dirlen = GetFinalPathNameByHandle(g_fds.p[dirfd].handle, dir, ARRAYLEN(dir), kNtFileNameNormalized | kNtVolumeNameDos); if (!dirlen) return __winerr(); - if (dirlen + 1 + filelen + 1 > ARRAYLEN(dir)) return enametoolong(); + if (dirlen + 1 + filelen + 1 > ARRAYLEN(dir)) { + SYSDEBUG("path too long: %.*hs\\%.*hs", dirlen, dir, filelen, file); + return enametoolong(); + } dir[dirlen] = u'\\'; memcpy(dir + dirlen + 1, file, (filelen + 1) * sizeof(char16_t)); memcpy(file, dir, (dirlen + 1 + filelen + 1) * sizeof(char16_t)); diff --git a/libc/calls/mprotect.greg.c b/libc/calls/mprotect.greg.c index dedc7a90999..00d7dfaa936 100644 --- a/libc/calls/mprotect.greg.c +++ b/libc/calls/mprotect.greg.c @@ -34,7 +34,7 @@ extern __msabi typeof(VirtualProtect) *const __imp_VirtualProtect; * @return 0 on success, or -1 w/ errno * @see mmap() */ -privileged int mprotect(void *addr, uint64_t len, int prot) { +noasan noubsan privileged int mprotect(void *addr, uint64_t len, int prot) { bool cf; int64_t rc; uint32_t oldprot; diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index 87356e77cfb..b4250508900 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -95,7 +95,7 @@ textwindows int ntspawn( } else { __winerr(); } - SYSDEBUG("CreateProcess(`%S`, `%S`) -> %d", prog16, block->cmdline, rc); + SYSDEBUG("CreateProcess(`%hs`, `%hs`) -> %d", prog16, block->cmdline, rc); } } else { __winerr(); diff --git a/libc/calls/openat-sysv.c b/libc/calls/openat-sysv.c index 3853be9c467..79d7a71cadb 100644 --- a/libc/calls/openat-sysv.c +++ b/libc/calls/openat-sysv.c @@ -20,6 +20,7 @@ #include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/o.h" @@ -59,9 +60,11 @@ int sys_openat(int dirfd, const char *file, int flags, unsigned mode) { } } if (d != -1) { - SYSDEBUG("sys_openat(0x%x, %s, %d, %d) -> %d", dirfd, file, flags, mode, d); + SYSDEBUG("sys_openat(%d, %s, %d, %d) -> %d", (long)dirfd, file, flags, + (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, d); } else { - SYSDEBUG("sys_openat(0x%x, %s, %d, %d) -> %m", dirfd, file, flags, mode); + SYSDEBUG("sys_openat(%d, %s, %d, %d) -> %s", (long)dirfd, file, flags, + (flags & (O_CREAT | O_TMPFILE)) ? mode : 0, strerror(errno)); } return d; } diff --git a/libc/calls/readansi.c b/libc/calls/readansi.c index d3249e16d9c..e7ee617fbf8 100644 --- a/libc/calls/readansi.c +++ b/libc/calls/readansi.c @@ -153,8 +153,17 @@ ssize_t readansi(int fd, char *p, size_t n) { } break; case kEsc: - if (0x20 <= c && c <= 0x2f) { - t = kNf; + if (0x20 <= c && c <= 0x2f) { /* Nf */ + /* + * Almost no one uses ANSI Nf sequences + * They overlaps with alt+graphic keystrokes + * We care more about being able to type alt-/ + */ + if (c == ' ' || c == '#') { + t = kNf; + } else { + t = kDone; + } } else if (0x30 <= c && c <= 0x3f) { /* Fp */ t = kDone; } else if (0x20 <= c && c <= 0x5F) { /* Fe */ @@ -173,8 +182,6 @@ ssize_t readansi(int fd, char *p, size_t n) { case '_': /* DCS (Application Program Command) */ t = kStr; break; - case '\\': - goto Whoopsie; default: t = kDone; break; diff --git a/libc/calls/readlinkat-nt.c b/libc/calls/readlinkat-nt.c index 9b860e44d28..13300dacaa5 100644 --- a/libc/calls/readlinkat-nt.c +++ b/libc/calls/readlinkat-nt.c @@ -20,6 +20,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" #include "libc/nt/createfile.h" @@ -37,7 +38,9 @@ static textwindows ssize_t sys_readlinkat_nt_error(void) { uint32_t e; - switch ((e = GetLastError())) { + e = GetLastError(); + SYSDEBUG("sys_readlinkat_nt() error %d", e); + switch (e) { case kNtErrorNotAReparsePoint: return einval(); default: @@ -56,7 +59,10 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, uint32_t e, i, j, n, mem; char16_t path16[PATH_MAX], *p; struct NtReparseDataBuffer *rdb; - if (!buf) return efault(); + if (__mkntpathat(dirfd, path, 0, path16) == -1) { + SYSDEBUG("sys_readlinkat_nt() failed b/c __mkntpathat() failed"); + return -1; + } if (weaken(malloc)) { mem = 16384; rdb = weaken(malloc)(mem); @@ -66,9 +72,9 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, rdb = (struct NtReparseDataBuffer *)buf; freeme = 0; } else { + SYSDEBUG("sys_readlinkat_nt() needs bigger buffer malloc() to be yoinked"); return enomem(); } - if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1; if ((h = CreateFile(path16, 0, 0, 0, kNtOpenExisting, kNtFileFlagOpenReparsePoint | kNtFileFlagBackupSemantics, 0)) != -1) { @@ -107,16 +113,20 @@ textwindows ssize_t sys_readlinkat_nt(int dirfd, const char *path, char *buf, if (freeme || (intptr_t)(buf + j) <= (intptr_t)(p + i)) { rc = j; } else { + SYSDEBUG("sys_readlinkat_nt() too many astral codepoints"); rc = enametoolong(); } } else { + SYSDEBUG("sys_readlinkat_nt() should have kNtIoReparseTagSymlink"); rc = einval(); } } else { + SYSDEBUG("DeviceIoControl(kNtFsctlGetReparsePoint) failed"); rc = sys_readlinkat_nt_error(); } CloseHandle(h); } else { + SYSDEBUG("CreateFile(kNtFileFlagOpenReparsePoint) failed"); rc = sys_readlinkat_nt_error(); } if (freeme && weaken(free)) { diff --git a/libc/calls/readlinkat.c b/libc/calls/readlinkat.c index 87332d0c85e..93e4dcf811c 100644 --- a/libc/calls/readlinkat.c +++ b/libc/calls/readlinkat.c @@ -19,8 +19,11 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/asan.internal.h" +#include "libc/str/str.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" @@ -46,14 +49,19 @@ * @asyncsignalsafe */ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) { + ssize_t bytes; struct ZiposUri zipname; - if (IsAsan() && !__asan_is_valid(buf, bufsiz)) return efault(); - if (weaken(__zipos_notat) && __zipos_notat(dirfd, path) == -1) { - return -1; /* TODO(jart): code me */ - } - if (!IsWindows()) { - return sys_readlinkat(dirfd, path, buf, bufsiz); + if ((IsAsan() && !__asan_is_valid(buf, bufsiz)) || (bufsiz && !buf)) { + bytes = efault(); + } else if (weaken(__zipos_notat) && __zipos_notat(dirfd, path) == -1) { + SYSDEBUG("TOOD: zipos support for readlinkat"); + bytes = enosys(); /* TODO(jart): code me */ + } else if (!IsWindows()) { + bytes = sys_readlinkat(dirfd, path, buf, bufsiz); } else { - return sys_readlinkat_nt(dirfd, path, buf, bufsiz); + bytes = sys_readlinkat_nt(dirfd, path, buf, bufsiz); } + SYSDEBUG("readlinkat(%d, %s, 0x%p, 0x%x) -> %d %s", (long)dirfd, path, buf, + bufsiz, bytes, bytes != -1 ? "" : strerror(errno)); + return bytes; } diff --git a/libc/calls/realpath.c b/libc/calls/realpath.c index b180f321ff9..fafa4dcdf7d 100644 --- a/libc/calls/realpath.c +++ b/libc/calls/realpath.c @@ -29,6 +29,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/errno.h" #include "libc/limits.h" #include "libc/mem/mem.h" @@ -160,6 +161,7 @@ char *realpath(const char *filename, char *resolved) if (!check_dir) goto skip_readlink; } k = readlink(output, stack, p); + if (k<0) SYSDEBUG("realpath readlink failed %d", (long)errno); if (k==p) goto toolong; if (!k) { errno = ENOENT; diff --git a/libc/calls/sigaltstack.c b/libc/calls/sigaltstack.c new file mode 100644 index 00000000000..6db0aba90db --- /dev/null +++ b/libc/calls/sigaltstack.c @@ -0,0 +1,106 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/struct/metasigaltstack.h" +#include "libc/calls/struct/sigaltstack.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/sysv/errfuns.h" + +static noasan void sigaltstack2bsd(struct sigaltstack_bsd *bsd, + const struct sigaltstack *linux) { + void *sp; + int flags; + size_t size; + sp = linux->ss_sp; + flags = linux->ss_flags; + size = linux->ss_size; + bsd->ss_sp = sp; + bsd->ss_flags = flags; + bsd->ss_size = size; +} + +static noasan void sigaltstack2linux(struct sigaltstack *linux, + const struct sigaltstack_bsd *bsd) { + void *sp; + int flags; + size_t size; + sp = bsd->ss_sp; + flags = bsd->ss_flags; + size = bsd->ss_size; + linux->ss_sp = sp; + linux->ss_flags = flags; + linux->ss_size = size; +} + +/** + * Sets and/or gets alternate signal stack, e.g. + * + * struct sigaction sa; + * struct sigaltstack ss; + * ss.ss_flags = 0; + * ss.ss_size = SIGSTKSZ; + * ss.ss_sp = malloc(ss.ss_size); + * sa.sa_flags = SA_ONSTACK; + * sa.sa_handler = OnStackOverflow; + * __cxa_atexit(free, ss[0].ss_sp, 0); + * sigemptyset(&sa.ss_mask); + * sigaltstack(&ss, 0); + * sigaction(SIGSEGV, &sa, 0); + * + * @param neu if non-null will install new signal alt stack + * @param old if non-null will receive current signal alt stack + * @return 0 on success, or -1 w/ errno + */ +noasan int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { + int rc; + void *a, *b; + struct sigaltstack_bsd bsd; + if (IsAsan() && ((old && __asan_check(old, sizeof(*old)).kind) || + (neu && (__asan_check(neu, sizeof(*neu)).kind || + __asan_check(neu->ss_sp, neu->ss_size).kind)))) { + return efault(); + } + if (IsLinux()) { + a = neu; + b = old; + } else if (IsBsd()) { + if (neu) { + sigaltstack2bsd(&bsd, neu); + a = &bsd; + } else { + a = 0; + } + if (old) { + b = &bsd; + } else { + b = 0; + } + } else { + return enosys(); + } + if ((rc = sys_sigaltstack(a, b)) != -1) { + if (old) { + sigaltstack2linux(old, &bsd); + } + return 0; + } else { + return -1; + } +} diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index 825c1427b55..05ad6075400 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -18,10 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/struct/metasigaltstack.h" #include "libc/calls/struct/siginfo.h" +#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" #include "libc/intrin/repstosb.h" #include "libc/str/str.h" +#include "libc/sysv/consts/sa.h" /** * @fileoverview XNU kernel callback normalization. @@ -45,12 +48,6 @@ struct __darwin_siginfo { uint64_t __pad[7]; }; -struct __darwin_sigaltstack { - void *ss_sp; - uint64_t ss_size; - int32_t ss_flags; -}; - struct __darwin_mmst_reg { char __mmst_reg[10]; char __mmst_rsrv[6]; @@ -368,7 +365,7 @@ struct __darwin_mcontext64 { struct __darwin_ucontext { int32_t uc_onstack; uint32_t uc_sigmask; - struct __darwin_sigaltstack uc_stack; + struct sigaltstack_bsd uc_stack; struct __darwin_ucontext *uc_link; uint64_t uc_mcsize; struct __darwin_mcontext64 *uc_mcontext; @@ -387,7 +384,7 @@ noasan static void linuxexceptionstate2xnu( } noasan static void xnuthreadstate2linux( - ucontext_t *uc, mcontext_t *mc, struct __darwin_x86_thread_state64 *xnuss) { + mcontext_t *mc, struct __darwin_x86_thread_state64 *xnuss) { mc->rdi = xnuss->__rdi; mc->rsi = xnuss->__rsi; mc->rbp = xnuss->__rbp; @@ -401,7 +398,6 @@ noasan static void xnuthreadstate2linux( mc->gs = xnuss->__gs; mc->fs = xnuss->__fs; mc->eflags = xnuss->__rflags; - uc->uc_flags = xnuss->__rflags; mc->r8 = xnuss->__r8; mc->r9 = xnuss->__r9; mc->r10 = xnuss->__r10; @@ -427,7 +423,6 @@ noasan static void linuxthreadstate2xnu( xnuss->__gs = mc->gs; xnuss->__fs = mc->fs; xnuss->__rflags = mc->eflags; - xnuss->__rflags = uc->uc_flags; xnuss->__r8 = mc->r8; xnuss->__r9 = mc->r9; xnuss->__r10 = mc->r10; @@ -484,6 +479,7 @@ noasan void __sigenter_xnu(void *fn, int infostyle, int sig, if (rva >= kSigactionMinRva) { repstosb(&g, 0, sizeof(g)); if (xnuctx) { + g.uc.uc_flags = xnuctx->uc_onstack ? SA_ONSTACK : 0; g.uc.uc_sigmask.__bits[0] = xnuctx->uc_sigmask; g.uc.uc_stack.ss_sp = xnuctx->uc_stack.ss_sp; g.uc.uc_stack.ss_flags = xnuctx->uc_stack.ss_flags; @@ -498,8 +494,7 @@ noasan void __sigenter_xnu(void *fn, int infostyle, int sig, if (xnuctx->uc_mcsize >= (sizeof(struct __darwin_x86_exception_state64) + sizeof(struct __darwin_x86_thread_state64))) { - xnuthreadstate2linux(&g.uc, &g.uc.uc_mcontext, - &xnuctx->uc_mcontext->__ss); + xnuthreadstate2linux(&g.uc.uc_mcontext, &xnuctx->uc_mcontext->__ss); } if (xnuctx->uc_mcsize >= sizeof(struct __darwin_mcontext64)) { xnussefpustate2linux(&g.uc.__fpustate, &xnuctx->uc_mcontext->__fs); diff --git a/libc/calls/struct/metasigaltstack.h b/libc/calls/struct/metasigaltstack.h new file mode 100644 index 00000000000..215bf4ec97a --- /dev/null +++ b/libc/calls/struct/metasigaltstack.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_METASIGALTSTACK_H_ +#define COSMOPOLITAN_LIBC_CALLS_STRUCT_METASIGALTSTACK_H_ +#include "libc/calls/struct/sigaltstack.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct sigaltstack_bsd { + void *ss_sp; + uint64_t ss_size; + int32_t ss_flags; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_METASIGALTSTACK_H_ */ diff --git a/libc/calls/struct/sigaction.h b/libc/calls/struct/sigaction.h index eff8931912a..1fcfb06af3a 100644 --- a/libc/calls/struct/sigaction.h +++ b/libc/calls/struct/sigaction.h @@ -14,7 +14,60 @@ struct sigaction { /* cosmo abi */ void (*sa_restorer)(void); struct sigset sa_mask; int64_t __pad; -} forcealign(8); +}; +sighandler_t signal(int, sighandler_t); +int sigaction(int, const struct sigaction *, struct sigaction *); + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +COSMOPOLITAN_C_START_ + +void _init_onntconsoleevent(void); +void _init_wincrash(void); + +#ifndef __SIGACTION_YOINK +#define __SIGACTION_YOINK(SIG) \ + do { \ + if (SupportsWindows()) { \ + if (__builtin_constant_p(SIG)) { \ + switch (SIG) { \ + case SIGINT: \ + case SIGQUIT: \ + case SIGHUP: \ + case SIGTERM: \ + YOINK(_init_onntconsoleevent); \ + break; \ + case SIGTRAP: \ + case SIGILL: \ + case SIGSEGV: \ + case SIGABRT: \ + case SIGFPE: \ + YOINK(_init_wincrash); \ + break; \ + default: \ + break; \ + } \ + } else { \ + YOINK(_init_onntconsoleevent); \ + YOINK(_init_wincrash); \ + } \ + } \ + } while (0) +#endif + +#define sigaction(SIG, ACT, OLD) \ + ({ \ + __SIGACTION_YOINK(SIG); \ + sigaction(SIG, (ACT), OLD); \ + }) + +#define signal(SIG, HAND) \ + ({ \ + __SIGACTION_YOINK(SIG); \ + signal(SIG, HAND); \ + }) + +#endif /* GNU && !ANSI */ +COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGACTION_H_ */ diff --git a/libc/calls/struct/sigaltstack.h b/libc/calls/struct/sigaltstack.h index 766cdfca558..3a7399459d6 100644 --- a/libc/calls/struct/sigaltstack.h +++ b/libc/calls/struct/sigaltstack.h @@ -1,6 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGALTSTACK_H_ #define COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGALTSTACK_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ struct sigaltstack { void *ss_sp; @@ -10,5 +11,8 @@ struct sigaltstack { typedef struct sigaltstack stack_t; +int sigaltstack(const struct sigaltstack *, struct sigaltstack *); + +COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_SIGALTSTACK_H_ */ diff --git a/libc/calls/unsetenv.c b/libc/calls/unsetenv.c index b38abc406a5..e2bfbc75ab9 100644 --- a/libc/calls/unsetenv.c +++ b/libc/calls/unsetenv.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" +#include "libc/mem/internal.h" #include "libc/runtime/runtime.h" /** @@ -29,6 +31,9 @@ int unsetenv(const char *s) { for (j = 0;; ++j) { if (!s[j]) { if (p[i][j] == '=') { + if (weaken(__freeenv)) { + weaken(__freeenv)(p[i]); + } k = i + 1; do { p[k - 1] = p[k]; diff --git a/libc/calls/winalarm.c b/libc/calls/winalarm.c index d3c27cda279..50265aef508 100644 --- a/libc/calls/winalarm.c +++ b/libc/calls/winalarm.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/struct/siginfo.h" +#include "libc/calls/typedef/sigaction_f.h" #include "libc/str/str.h" #include "libc/sysv/consts/sig.h" diff --git a/libc/calls/wincrash.c b/libc/calls/wincrash.c index a49cc25e593..32259c4d1dc 100644 --- a/libc/calls/wincrash.c +++ b/libc/calls/wincrash.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/sysdebug.internal.h" +#include "libc/calls/typedef/sigaction_f.h" #include "libc/calls/ucontext.h" #include "libc/nt/enum/exceptionhandleractions.h" #include "libc/nt/enum/signal.h" diff --git a/libc/calls/winerr.greg.c b/libc/calls/winerr.greg.c index 5fc3e4e6aab..45c8d37c494 100644 --- a/libc/calls/winerr.greg.c +++ b/libc/calls/winerr.greg.c @@ -16,10 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/weaken.h" #include "libc/dce.h" #include "libc/errno.h" #include "libc/nt/errors.h" #include "libc/nt/runtime.h" +#include "libc/sock/internal.h" #include "libc/sysv/errfuns.h" /** @@ -29,8 +31,13 @@ * @note this is a code-size saving device */ privileged noasan int64_t __winerr(void) { + errno_t e; if (IsWindows()) { - errno = GetLastError(); + e = GetLastError(); + if (weaken(__dos2errno)) { + e = weaken(__dos2errno)(e); + } + errno = e; return -1; } else { return enosys(); diff --git a/libc/dns/lookupprotobynumber.c b/libc/dns/lookupprotobynumber.c index c543b047c27..6d455d4e810 100644 --- a/libc/dns/lookupprotobynumber.c +++ b/libc/dns/lookupprotobynumber.c @@ -85,6 +85,7 @@ int LookupProtoByNumber(const int protonum, char *buf, size_t bufsize, free(line); if (ferror(f)) { errno = ferror(f); + fclose(f); return -1; } fclose(f); diff --git a/libc/fmt/conv.h b/libc/fmt/conv.h index 5b6e8758c2a..66c42bde6b3 100644 --- a/libc/fmt/conv.h +++ b/libc/fmt/conv.h @@ -17,7 +17,6 @@ COSMOPOLITAN_C_START_ int abs(int) libcesque pureconst; long labs(long) libcesque pureconst; long long llabs(long long) libcesque pureconst; -int llog10(unsigned long) libcesque pureconst; int atoi(const char *) paramsnonnull() libcesque; long atol(const char *) paramsnonnull() libcesque; long long atoll(const char *) paramsnonnull() libcesque; diff --git a/libc/fmt/fmt.c b/libc/fmt/fmt.c index 55130f1342f..5fb41731978 100644 --- a/libc/fmt/fmt.c +++ b/libc/fmt/fmt.c @@ -42,6 +42,15 @@ static const char kSpecialFloats[2][2][4] = {{"INF", "inf"}, {"NAN", "nan"}}; +static void __fmt_free_dtoa(char **mem) { + if (*mem) { + if (weaken(freedtoa)) { + weaken(freedtoa)(*mem); + } + *mem = 0; + } +} + static int __fmt_atoi(const char **str) { int i; for (i = 0; '0' <= **str && **str <= '9'; ++*str) { @@ -135,11 +144,12 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { int (*out)(const char *, void *, size_t); unsigned char signbit, log2base; int c, d, k, w, n, i1, ui, bw, bex; - char *s, *q, *se, qchar, special[8]; + char *s, *q, *se, *mem, qchar, special[8]; int sgn, alt, sign, prec, prec1, flags, width, decpt, lasterr; lasterr = errno; out = fn ? fn : (void *)missingno; + mem = 0; while (*format) { if (*format != '%') { @@ -388,7 +398,8 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { p = "?"; goto FormatThatThing; } - s = weaken(__fmt_dtoa)(pun.d, 3, prec, &decpt, &sgn, &se); + assert(!mem); + s = mem = weaken(__fmt_dtoa)(pun.d, 3, prec, &decpt, &sgn, &se); if (decpt == 9999) { Format9999: bzero(special, sizeof(special)); @@ -402,6 +413,8 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { } memcpy(q, kSpecialFloats[*s == 'N'][d >= 'a'], 4); FormatThatThing: + __fmt_free_dtoa(&mem); + mem = 0; prec = alt = 0; flags &= ~(FLAGS_PRECISION | FLAGS_PLUS | FLAGS_SPACE); goto FormatString; @@ -462,6 +475,7 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { while (--width >= 0) { PUT(' '); } + __fmt_free_dtoa(&mem); continue; case 'G': @@ -477,7 +491,9 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { p = "?"; goto FormatThatThing; } - s = weaken(__fmt_dtoa)(pun.d, prec ? 2 : 0, prec, &decpt, &sgn, &se); + assert(!mem); + s = mem = + weaken(__fmt_dtoa)(pun.d, prec ? 2 : 0, prec, &decpt, &sgn, &se); if (decpt == 9999) goto Format9999; c = se - s; prec1 = prec; @@ -512,7 +528,8 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { p = "?"; goto FormatThatThing; } - s = weaken(__fmt_dtoa)(pun.d, 2, prec + 1, &decpt, &sgn, &se); + assert(!mem); + s = mem = weaken(__fmt_dtoa)(pun.d, 2, prec + 1, &decpt, &sgn, &se); if (decpt == 9999) goto Format9999; FormatExpo: if (sgn) sign = '-'; @@ -547,6 +564,7 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { } PUT(c); } + __fmt_free_dtoa(&mem); PUT(d); if (decpt < 0) { PUT('-'); @@ -721,5 +739,7 @@ hidden int __fmt(void *fn, void *arg, const char *format, va_list va) { break; } } + + assert(!mem); return 0; } diff --git a/libc/fmt/itoa.h b/libc/fmt/itoa.h index 3fda5a959c8..5cfbc14668b 100644 --- a/libc/fmt/itoa.h +++ b/libc/fmt/itoa.h @@ -16,6 +16,10 @@ COSMOPOLITAN_C_START_ - uint128toarray_radix10(0x31337, a) l: 93 (27ns) m: 141 (41ns) - int128toarray_radix10(0x31337, a) l: 96 (28ns) m: 173 (51ns) */ +unsigned LengthInt64(int64_t) pureconst; +unsigned LengthUint64(uint64_t) pureconst; +unsigned LengthInt64Thousands(int64_t) pureconst; +unsigned LengthUint64Thousands(uint64_t) pureconst; char *FormatInt32(char[hasatleast 12], int32_t); char *FormatUint32(char[hasatleast 12], uint32_t); char *FormatInt64(char[hasatleast 21], int64_t); diff --git a/libc/fmt/kdos2errno.S b/libc/fmt/kdos2errno.S new file mode 100644 index 00000000000..8c9789855fa --- /dev/null +++ b/libc/fmt/kdos2errno.S @@ -0,0 +1,102 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nt/errors.h" +#include "libc/macros.internal.h" + +// @fileoverview data structure for __dos2errno() + + .macro .e doscode systemv + .short \doscode + .long \systemv - kDos2Errno + .endm + + .section .rodata +kDos2Errno: + .e kNtErrorModNotFound,ENOSYS + .e kNtErrorBadCommand,EACCES + .e kNtErrorBadLength,EACCES + .e kNtErrorBadNetpath,ENOENT + .e kNtErrorBadNetName,ENOENT + .e kNtErrorBadNetResp,ENETDOWN + .e kNtErrorBadPathname,ENOENT + .e kNtErrorCannotMake,EACCES + .e kNtErrorCommitmentLimit,ENOMEM + .e kNtErrorConnectionAborted,ECONNABORTED + .e kNtErrorConnectionActive,EISCONN + .e kNtErrorConnectionRefused,ECONNREFUSED + .e kNtErrorCrc,EACCES + .e kNtErrorDirNotEmpty,ENOTEMPTY + .e kNtErrorDupName,EADDRINUSE + .e kNtErrorFilenameExcedRange,ENOENT + .e kNtErrorGenFailure,EACCES + .e kNtErrorGracefulDisconnect,EPIPE + .e kNtErrorHostDown,EHOSTUNREACH + .e kNtErrorHostUnreachable,EHOSTUNREACH + .e kNtErrorInsufficientBuffer,EFAULT + .e kNtErrorInvalidAddress,EADDRNOTAVAIL + .e kNtErrorInvalidFunction,EINVAL + .e kNtErrorInvalidNetname,EADDRNOTAVAIL + .e kNtErrorInvalidUserBuffer,EMSGSIZE + .e kNtErrorIoPending,EINPROGRESS + .e kNtErrorLockViolation,EACCES + .e kNtErrorMoreData,EMSGSIZE + .e kNtErrorNetnameDeleted,ECONNABORTED + .e kNtErrorNetworkAccessDenied,EACCES + .e kNtErrorNetworkBusy,ENETDOWN + .e kNtErrorNetworkUnreachable,ENETUNREACH + .e kNtErrorNoaccess,EFAULT + .e kNtErrorNonpagedSystemResources,ENOMEM + .e kNtErrorNotEnoughMemory,ENOMEM + .e kNtErrorNotEnoughQuota,ENOMEM + .e kNtErrorNotFound,ENOENT + .e kNtErrorNotLocked,EACCES + .e kNtErrorNotReady,EACCES + .e kNtErrorNotSupported,ENOTSUP + .e kNtErrorNoMoreFiles,ENOENT + .e kNtErrorNoSystemResources,ENOMEM + .e kNtErrorOperationAborted,EINTR + .e kNtErrorOutOfPaper,EACCES + .e kNtErrorPagedSystemResources,ENOMEM + .e kNtErrorPagefileQuota,ENOMEM + .e kNtErrorPipeNotConnected,EPIPE + .e kNtErrorPortUnreachable,ECONNRESET + .e kNtErrorProtocolUnreachable,ENETUNREACH + .e kNtErrorRemNotList,ECONNREFUSED + .e kNtErrorRequestAborted,EINTR + .e kNtErrorReqNotAccep,EWOULDBLOCK + .e kNtErrorSectorNotFound,EACCES + .e kNtErrorSemTimeout,ETIMEDOUT + .e kNtErrorSharingViolation,EACCES + .e kNtErrorTooManyNames,ENOMEM + .e kNtErrorUnexpNetErr,ECONNABORTED + .e kNtErrorWorkingSetQuota,ENOMEM + .e kNtErrorWriteProtect,EACCES + .e kNtErrorWrongDisk,EACCES + .e WSAEACCES,EACCES + .e WSAEDISCON,EPIPE + .e WSAEFAULT,EFAULT + .e WSAEINPROGRESS,EBUSY + .e WSAEINVAL,EINVAL + .e WSAEPROCLIM,ENOMEM + .e WSAESHUTDOWN,EPIPE + .e WSANOTINITIALISED,ENETDOWN + .e WSASYSNOTREADY,ENETDOWN + .e WSAVERNOTSUPPORTED,ENOSYS + .short 0 + .endobj kDos2Errno,globl,hidden diff --git a/libc/fmt/lengthuint64.c b/libc/fmt/lengthuint64.c new file mode 100644 index 00000000000..937b7567bb8 --- /dev/null +++ b/libc/fmt/lengthuint64.c @@ -0,0 +1,98 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" + +static const uint64_t kTens[] = { + 1ull, + 10ull, + 100ull, + 1000ull, + 10000ull, + 100000ull, + 1000000ull, + 10000000ull, + 100000000ull, + 1000000000ull, + 10000000000ull, + 100000000000ull, + 1000000000000ull, + 10000000000000ull, + 100000000000000ull, + 1000000000000000ull, + 10000000000000000ull, + 100000000000000000ull, + 1000000000000000000ull, + 10000000000000000000ull, +}; + +static const unsigned char kTensIndex[] = { + 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, // + 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, // + 10, 10, 10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, // + 15, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 18, 19, 19, // +}; + +/** + * Returns `len(str(x))` where x is an unsigned 64-bit integer. + */ +unsigned LengthUint64(uint64_t x) { + unsigned w; + if (x) { + w = kTensIndex[63 ^ __builtin_clzll(x)]; + w += x >= kTens[w]; + return w; + } else { + return 1; + } +} + +/** + * Returns `len(str(x))` where x is a signed 64-bit integer. + */ +unsigned LengthInt64(int64_t x) { + if (x >= 0) { + return LengthUint64(x); + } else { + return 1 + LengthUint64(-(uint64_t)x); + } +} + +/** + * Returns decimal string length of uint64 w/ thousands separators. + */ +unsigned LengthUint64Thousands(uint64_t x) { + unsigned w; + w = LengthUint64(x); + w += (w - 1) / 3; + return w; +} + +/** + * Returns decimal string length of int64 w/ thousands separators. + */ +unsigned LengthInt64Thousands(int64_t x) { + unsigned w; + if (x >= 0) { + w = LengthUint64(x); + return w + (w - 1) / 3; + } else { + w = LengthUint64(-(uint64_t)x); + return 1 + w + (w - 1) / 3; + } +} diff --git a/libc/fmt/mapdoserrortoerrno.c b/libc/fmt/mapdoserrortoerrno.c new file mode 100644 index 00000000000..989a5ef990e --- /dev/null +++ b/libc/fmt/mapdoserrortoerrno.c @@ -0,0 +1,41 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/nt/errors.h" +#include "libc/sock/sock.h" + +struct thatispacked Dos2Errno { + uint16_t doscode; + int32_t systemv; +}; + +extern const struct Dos2Errno kDos2Errno[]; + +/** + * Translates Windows error using superset of consts.sh. + */ +textwindows errno_t __dos2errno(uint32_t error) { + int i; + for (i = 0; kDos2Errno[i].doscode; ++i) { + if (error == kDos2Errno[i].doscode) { + return *(const int *)((intptr_t)kDos2Errno + kDos2Errno[i].systemv); + } + } + return error; +} diff --git a/libc/fmt/ntoa.c b/libc/fmt/ntoa.c index 508113de768..488dffca67c 100644 --- a/libc/fmt/ntoa.c +++ b/libc/fmt/ntoa.c @@ -21,6 +21,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/fmts.h" #include "libc/fmt/internal.h" +#include "libc/limits.h" #define BUFFER_SIZE 144 @@ -31,7 +32,6 @@ static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, unsigned log2base, unsigned prec, unsigned width, unsigned char flags) { unsigned i; - /* pad leading zeros */ if (!(flags & FLAGS_LEFT)) { if (width && (flags & FLAGS_ZEROPAD) && @@ -45,7 +45,6 @@ static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, buf[len++] = '0'; } } - /* handle hash */ if (flags & FLAGS_HASH) { if (!(flags & FLAGS_PRECISION) && len && @@ -64,7 +63,6 @@ static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, buf[len++] = '0'; } } - if (len < BUFFER_SIZE) { if (negative) { buf[len++] = '-'; @@ -74,17 +72,14 @@ static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, buf[len++] = ' '; } } - /* pad spaces up to given width */ if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { if (len < width) { if (__fmt_pad(out, arg, width - len) == -1) return -1; } } - reverse(buf, len); if (out(buf, arg, len) == -1) return -1; - /* append pad spaces up to given width */ if (flags & FLAGS_LEFT) { if (len < width) { @@ -97,6 +92,7 @@ static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, int __fmt_ntoa2(int out(const char *, void *, size_t), void *arg, uintmax_t value, bool neg, unsigned log2base, unsigned prec, unsigned width, unsigned flags, const char *alphabet) { + uint64_t u64; uintmax_t remainder; unsigned len, count, digit; char buf[BUFFER_SIZE]; @@ -105,10 +101,15 @@ int __fmt_ntoa2(int out(const char *, void *, size_t), void *arg, if (value || !(flags & FLAGS_PRECISION)) { count = 0; do { - assert(len < BUFFER_SIZE); if (!log2base) { - value = __udivmodti4(value, 10, &remainder); - digit = remainder; + if (value <= UINT64_MAX) { + u64 = value; + digit = u64 % 10; + value = u64 / 10; + } else { + value = __udivmodti4(value, 10, &remainder); + digit = remainder; + } } else { digit = value; digit &= (1u << log2base) - 1; @@ -122,6 +123,7 @@ int __fmt_ntoa2(int out(const char *, void *, size_t), void *arg, } buf[len++] = alphabet[digit]; } while (value); + assert(count <= BUFFER_SIZE); } return __fmt_ntoa_format(out, arg, buf, len, neg, log2base, prec, width, flags); diff --git a/libc/fmt/strerror.c b/libc/fmt/strerror.c index 28a658e37c8..d5d610a3a90 100644 --- a/libc/fmt/strerror.c +++ b/libc/fmt/strerror.c @@ -22,7 +22,7 @@ * Converts errno value to string non-reentrantly. * @see strerror_r() */ -char *strerror(int err) { +noasan char *strerror(int err) { _Alignas(1) static char buf[512]; strerror_r(err, buf, sizeof(buf)); return buf; diff --git a/libc/fmt/strerror_r.c b/libc/fmt/strerror_r.c index fcabd98be36..dc024cb6a9c 100644 --- a/libc/fmt/strerror_r.c +++ b/libc/fmt/strerror_r.c @@ -21,11 +21,20 @@ #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/fmt/itoa.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" +#include "libc/nexgen32e/bsr.h" #include "libc/nt/enum/formatmessageflags.h" +#include "libc/nt/enum/lang.h" +#include "libc/nt/memory.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" #include "libc/str/str.h" +#include "libc/str/tpenc.h" + +#if !IsTiny() +STATIC_YOINK("__dos2errno"); +#endif struct Error { int x; @@ -35,7 +44,7 @@ struct Error { extern const struct Error kErrorNames[]; extern const struct Error kErrorNamesLong[]; -static const char *GetErrorName(long x) { +noasan static inline const char *GetErrorName(long x) { int i; if (x) { for (i = 0; kErrorNames[i].x; ++i) { @@ -47,7 +56,7 @@ static const char *GetErrorName(long x) { return "EUNKNOWN"; } -static const char *GetErrorNameLong(long x) { +noasan static inline const char *GetErrorNameLong(long x) { int i; if (x) { for (i = 0; kErrorNamesLong[i].x; ++i) { @@ -65,24 +74,51 @@ static const char *GetErrorNameLong(long x) { * Converts errno value to string. * @return 0 on success, or error code */ -int strerror_r(int err, char *buf, size_t size) { - char *p; +noasan int strerror_r(int err, char *buf, size_t size) { + uint64_t w; + int c, i, n; + char *p, *e; const char *s; - err &= 0xFFFF; - if (IsTiny()) { - s = GetErrorName(err); - } else { - s = GetErrorNameLong(err); - } + char16_t *ws = 0; p = buf; - if (strlen(s) + 1 + 5 + 1 + 1 <= size) { - p = stpcpy(p, s); - *p++ = '['; - p += uint64toarray_radix10(err, p); - *p++ = ']'; + e = p + size; + err &= 0xFFFF; + s = IsTiny() ? GetErrorName(err) : GetErrorNameLong(err); + while ((c = *s++)) { + if (p + 1 + 1 <= e) *p++ = c; } - if (p - buf < size) { - *p++ = '\0'; + if (!IsTiny()) { + if (p + 1 + 5 + 1 + 1 <= e) { + *p++ = '['; + p = __intcpy(p, err); + *p++ = ']'; + } + if (IsWindows()) { + err = GetLastError() & 0xffff; + if ((n = FormatMessage( + kNtFormatMessageAllocateBuffer | kNtFormatMessageFromSystem | + kNtFormatMessageIgnoreInserts, + 0, err, MAKELANGID(kNtLangNeutral, kNtSublangDefault), + (char16_t *)&ws, 0, 0))) { + while (n && ws[n - 1] <= L' ' || ws[n - 1] == L'.') --n; + if (p + 1 + 1 <= e) *p++ = '['; + for (i = 0; i < n; ++i) { + w = tpenc(ws[i] & 0xffff); + if (p + (bsrll(w) >> 3) + 1 + 1 <= e) { + do *p++ = w; + while ((w >>= 8)); + } + } + if (p + 1 + 1 <= e) *p++ = ']'; + LocalFree(ws); + } + if (p + 1 + 5 + 1 + 1 <= e) { + *p++ = '['; + p = __intcpy(p, err); + *p++ = ']'; + } + } } + if (p + 1 <= e) *p = 0; return 0; } diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 066eb6a50d3..c6004586acf 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -157,7 +157,7 @@ typedef uint64_t uintmax_t; #define strlenesque libcesque nosideeffect paramsnonnull() #define vallocesque \ libcesque nodiscard returnsaligned((PAGESIZE)) returnspointerwithnoaliases -#define reallocesque libcesque returnsaligned((__BIGGEST_ALIGNMENT__)) +#define reallocesque libcesque returnsaligned((16)) #define mallocesque reallocesque returnspointerwithnoaliases #define interruptfn nocallersavedregisters forcealignargpointer diff --git a/libc/integral/normalize.inc b/libc/integral/normalize.inc index 0c1d43d8e4f..489d5a7f407 100644 --- a/libc/integral/normalize.inc +++ b/libc/integral/normalize.inc @@ -64,7 +64,7 @@ #endif #define BIGPAGESIZE 0x200000 -#define STACKSIZE 0x200000 +#define STACKSIZE 0x20000 #define FRAMESIZE 0x10000 /* 8086 */ #define PAGESIZE 0x1000 /* i386+ */ #define BUFSIZ 0x1000 /* best stdio default */ diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index a538d6b3cc1..78aedd9513c 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -17,32 +17,38 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/reverse.internal.h" +#include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/bits/likely.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/struct/iovec.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/log/backtrace.internal.h" +#include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/hook/hook.internal.h" +#include "libc/nexgen32e/gc.internal.h" +#include "libc/nexgen32e/stackframe.h" #include "libc/nt/enum/version.h" #include "libc/nt/runtime.h" #include "libc/runtime/directmap.internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/errfuns.h" #include "third_party/dlmalloc/dlmalloc.internal.h" -#define COOKIE 21578 - STATIC_YOINK("_init_asan"); /** @@ -83,15 +89,25 @@ STATIC_YOINK("_init_asan"); } \ } while (0) -#define REQUIRE(FUNC) \ - do { \ - if (!weaken(FUNC)) { \ - __asan_die("error: asan needs " #FUNC "\n"); \ - } \ +#define REQUIRE(FUNC) \ + do { \ + if (!weaken(FUNC)) { \ + __asan_die("error: asan needs " #FUNC "\n")(); \ + __asan_unreachable(); \ + } \ } while (0) typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); +struct AsanTrace { + intptr_t p[4]; +}; + +struct AsanExtra { + uint64_t size; + struct AsanTrace bt; +}; + struct AsanSourceLocation { const char *filename; int line; @@ -122,78 +138,36 @@ struct AsanMorgue { void *p[32]; }; +bool __asan_noreentry; static struct AsanMorgue __asan_morgue; -static inline int __asan_bsrl(uint64_t x) { - return __builtin_clzll(x) ^ 63; -} - -static uint64_t __asan_roundup2pow(uint64_t x) { - return x > 1 ? 1ull << (__asan_bsrl(x - 1) + 1) : x ? 1 : 0; -} - -static uint64_t __asan_rounddown2pow(uint64_t x) { - return x ? 1ull << __asan_bsrl(x) : 0; +static wontreturn void __asan_unreachable(void) { + for (;;) __builtin_trap(); } -static uint64_t __asan_encodeutf8(unsigned c) { - static const unsigned short kTpEnc[32 - 7] = { - 1 | 0300 << 8, 1 | 0300 << 8, 1 | 0300 << 8, 1 | 0300 << 8, 2 | 0340 << 8, - 2 | 0340 << 8, 2 | 0340 << 8, 2 | 0340 << 8, 2 | 0340 << 8, 3 | 0360 << 8, - 3 | 0360 << 8, 3 | 0360 << 8, 3 | 0360 << 8, 3 | 0360 << 8, 4 | 0370 << 8, - 4 | 0370 << 8, 4 | 0370 << 8, 4 | 0370 << 8, 4 | 0370 << 8, 5 | 0374 << 8, - 5 | 0374 << 8, 5 | 0374 << 8, 5 | 0374 << 8, 5 | 0374 << 8, 5 | 0374 << 8, - }; - int e, n; - unsigned long long w; - if (c < 0200) return c; - e = kTpEnc[__asan_bsrl(c) - 7]; - n = e & 0xff; - w = 0; - do { - w |= 0200 | (c & 077); - w <<= 8; - c >>= 6; - } while (--n); - return c | w | e >> 8; +static int __asan_bsf(uint64_t x) { + _Static_assert(sizeof(long long) == sizeof(uint64_t), ""); + return __builtin_ctzll(x); } -static size_t __asan_strlen(const char *s) { - size_t n = 0; - while (*s++) ++n; - return n; -} - -static int __asan_strcmp(const char *l, const char *r) { - size_t i = 0; - while (l[i] == r[i] && r[i]) ++i; - return (l[i] & 255) - (r[i] & 255); -} - -static char *__asan_stpcpy(char *d, const char *s) { - size_t i; - for (i = 0;; ++i) { - if (!(d[i] = s[i])) { - return d + i; - } - } +static int __asan_bsr(uint64_t x) { + _Static_assert(sizeof(long long) == sizeof(uint64_t), ""); + return __builtin_clzll(x) ^ 63; } -static void *__asan_repstosb(void *di, int al, size_t cx) { - asm("rep stosb" - : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) - : "0"(di), "1"(cx), "a"(al)); - return di; +static uint64_t __asan_roundup2pow(uint64_t x) { + return 2ull << __asan_bsr(x - 1); } -static void *__asan_repmovsb(void *di, void *si, size_t cx) { - asm("rep movsb" - : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) - : "0"(di), "1"(si), "2"(cx), "m"(*(char(*)[cx])si)); - return di; +static char *__asan_utf8cpy(char *p, unsigned c) { + uint64_t z; + z = tpenc(c); + do *p++ = z; + while ((z >>= 8)); + return p; } -static void *__asan_memset(void *p, int c, size_t n) { +static void *__asan_memset(void *p, char c, size_t n) { char *b; size_t i; uint64_t x; @@ -245,7 +219,7 @@ static void *__asan_memset(void *p, int c, size_t n) { } while ((i += 16) + 16 <= n); for (; i < n; ++i) b[i] = x; } else { - __asan_repstosb(p, c, n); + __repstosb(p, c, n); } return p; } @@ -313,7 +287,7 @@ static void *__asan_mempcpy(void *dst, const void *src, size_t n) { for (; i < n; ++i) d[i] = s[i]; return d + i; } else { - return __asan_repmovsb(d, s, n); + return __repmovsb(d, s, n); } } } @@ -328,121 +302,58 @@ static char *__asan_hexcpy(char *p, uint64_t x, uint8_t k) { return p; } -static char *__asan_uintcpy(char p[hasatleast 21], uint64_t x) { - char t; - size_t i, a, b; - i = 0; - do { - p[i++] = x % 10 + '0'; - x = x / 10; - } while (x > 0); - p[i] = '\0'; - if (i) { - for (a = 0, b = i - 1; a < b; ++a, --b) { - t = p[a]; - p[a] = p[b]; - p[b] = t; - } - } - return p + i; -} - -static char *__asan_intcpy(char p[hasatleast 21], int64_t x) { - if (x < 0) *p++ = '-', x = -(uint64_t)x; - return __asan_uintcpy(p, x); -} - -privileged noinline wontreturn void __asan_exit(int rc) { - if (!IsWindows()) { - asm volatile("syscall" - : /* no outputs */ - : "a"(__NR_exit_group), "D"(rc) - : "memory"); - unreachable; - } else { - ExitProcess(rc); - } -} - -privileged noinline ssize_t __asan_write(const void *data, size_t size) { - ssize_t rc; - uint32_t wrote; - if (!IsWindows()) { - asm volatile("syscall" - : "=a"(rc) - : "0"(__NR_write), "D"(2), "S"(data), "d"(size) - : "rcx", "r11", "memory"); - return rc; - } else { - if (WriteFile(GetStdHandle(kNtStdErrorHandle), data, size, &wrote, 0)) { - return wrote; - } else { - return -1; - } - } +static void __asan_exit(void) { + __printf("your asan runtime needs\n" + "\tSTATIC_YOINK(\"__die\");\n" + "in order to show you backtraces\n"); + _Exit(99); } -static ssize_t __asan_write_string(const char *s) { - return __asan_write(s, __asan_strlen(s)); -} - -wontreturn void __asan_die(const char *msg) { - __asan_write_string(msg); +nodiscard static __asan_die_f *__asan_die(const char *msg) { + __printf("%s", msg); if (weaken(__die)) { - weaken(__die)(); + return weaken(__die); } else { - __printf("this binary needs\n" - "\tSTATIC_YOINK(\"__die\");\n" - "if you want to see backtraces\n"); + return __asan_exit; } - __asan_exit(134); } -void __asan_poison(uintptr_t p, size_t n, int t) { +void __asan_poison(long p, long n, signed char t) { signed char k, *s; - k = p & 7; s = (signed char *)((p >> 3) + 0x7fff8000); - if (UNLIKELY(k)) { - if (k < *s && *s <= k + n) *s = k; + if ((k = p & 7)) { + if ((!*s && n >= 8 - k) || *s > k) *s = k; n -= MIN(8 - k, n); s += 1; } __asan_memset(s, t, n >> 3); if ((k = n & 7)) { s += n >> 3; - if (*s >= 0) { - *s = kAsanHeapOverrun; - } + if (*s < 0 || (*s > 0 && *s <= k)) *s = t; } } -void __asan_unpoison(uintptr_t p, size_t n) { +void __asan_unpoison(long p, long n) { signed char k, *s; k = p & 7; s = (signed char *)((p >> 3) + 0x7fff8000); if (UNLIKELY(k)) { - if (!n) return; if (k + n < 8) { - *s = MAX(*s, k + n); + if (n > 0) *s = MAX(*s, k + n); return; - } else { - *s = 0; } n -= MIN(8 - k, n); - s += 1; + *s++ = 0; } __asan_memset(s, 0, n >> 3); if ((k = n & 7)) { s += n >> 3; - if (*s < 0) { - *s = k; - } else if (*s > 0) { - *s = MAX(*s, k); - } + if (*s < 0) *s = k; + if (*s > 0) *s = MAX(*s, k); } } -static inline bool __asan_is_mapped(int x) { +static bool __asan_is_mapped(int x) { int i; struct MemoryIntervals *m; m = weaken(_mmi); @@ -450,15 +361,15 @@ static inline bool __asan_is_mapped(int x) { return i < m->i && m->p[i].x <= x && x <= m->p[i].y; } -static inline bool __asan_is_image(const unsigned char *p) { +static bool __asan_is_image(const unsigned char *p) { return _base <= p && p < _end; } -static inline bool __asan_exists(const void *x) { +static bool __asan_exists(const void *x) { return __asan_is_image(x) || __asan_is_mapped((intptr_t)x >> 16); } -static struct AsanFault __asan_fault(signed char *s, char dflt) { +static struct AsanFault __asan_fault(const signed char *s, signed char dflt) { struct AsanFault r; if (s[0] < 0) { r.kind = s[0]; @@ -471,35 +382,12 @@ static struct AsanFault __asan_fault(signed char *s, char dflt) { return r; } -struct AsanFault __asan_check(const void *p, size_t n) { +static struct AsanFault __asan_checka(const signed char *s, long ndiv8) { intptr_t a; uint64_t w; - unsigned u, r; - signed char k, *s, *e, *f; - if (!n) return (struct AsanFault){0}; - k = (intptr_t)p & 7; - a = ((intptr_t)p >> 3) + 0x7fff8000; - s = (signed char *)a; - if (!__asan_is_mapped(a >> 16)) { - return (struct AsanFault){kAsanUnmapped, s}; - } - if (UNLIKELY(k)) { - if (!*s) { - n -= MIN(8 - k, n); - s += 1; - } else if (*s > 0 && k + n < 8 && *s >= k + n) { - return (struct AsanFault){0}; - } else { - return __asan_fault(s, kAsanHeapOverrun); - } - } - e = s; - k = n & 7; - e += n >> 3; - while (s < e && ((intptr_t)s & 7)) { - if (*s++) { - return __asan_fault(s - 1, kAsanHeapOverrun); - } + signed char c, *e = s + ndiv8; + for (; ((intptr_t)s & 7) && s < e; ++s) { + if (*s) return __asan_fault(s - 1, kAsanHeapOverrun); } for (; s + 8 <= e; s += 8) { if (UNLIKELY(!((a = (intptr_t)s) & 0xffff))) { @@ -511,36 +399,71 @@ struct AsanFault __asan_check(const void *p, size_t n) { (uint64_t)(255 & s[2]) << 020 | (uint64_t)(255 & s[3]) << 030 | (uint64_t)(255 & s[4]) << 040 | (uint64_t)(255 & s[5]) << 050 | (uint64_t)(255 & s[6]) << 060 | (uint64_t)(255 & s[7]) << 070))) { - s += (unsigned)__builtin_ctzll(w) >> 3; + s += __asan_bsf(w) >> 3; return __asan_fault(s, kAsanHeapOverrun); } } - while (s < e) { - if (*s++) { - return __asan_fault(s - 1, kAsanHeapOverrun); - } - } - if (!k || !*s || k <= *s) { - return (struct AsanFault){0}; - } else { - return __asan_fault(s, kAsanHeapOverrun); + for (; s < e; ++s) { + if (*s) return __asan_fault(s - 1, kAsanHeapOverrun); } + return (struct AsanFault){0}; } -void __asan_verify(const void *p, size_t n) { - const char *q; +/** + * Checks validity of memory range. + * + * Normally this is abstracted by the compiler. + * + * @param p is starting virtual address + * @param n is number of bytes to check + * @return kind is 0 on success or <0 on invalid + * @return shadow points to first poisoned shadow byte + * @note this takes 6 picoseconds per byte + */ +struct AsanFault __asan_check(const void *p, long n) { + intptr_t a; + uint64_t w; struct AsanFault f; - if (!(f = __asan_check(p, n)).kind) return; - q = UNSHADOW(f.shadow); - if ((uintptr_t)q != ((uintptr_t)p & -8) && (uintptr_t)q - (uintptr_t)p < n) { - n -= (uintptr_t)q - (uintptr_t)p; - p = q; + signed char c, k, *s; + if (n > 0) { + k = (intptr_t)p & 7; + a = ((intptr_t)p >> 3) + 0x7fff8000; + s = (signed char *)a; + if (OverlapsShadowSpace(p, n)) { + return (struct AsanFault){kAsanProtected, s}; + } else if (!(0 <= a && a <= 0x7fffffffffff) && !__asan_is_mapped(a >> 16)) { + return (struct AsanFault){kAsanUnmapped, s}; + } + if (UNLIKELY(k)) { + if (!(c = *s)) { + n -= MIN(8 - k, n); + s += 1; + } else if (c > 0 && n < 8 && c >= k + n) { + return (struct AsanFault){0}; + } else { + return __asan_fault(s, kAsanHeapOverrun); + } + } + k = n & 7; + n >>= 3; + if ((f = __asan_checka(s, n)).kind) { + return f; + } else if (!k || !(c = s[n]) || k <= c) { + return (struct AsanFault){0}; + } else { + return __asan_fault(s, kAsanHeapOverrun); + } + } else if (!n) { + return (struct AsanFault){0}; + } else { + return (struct AsanFault){kAsanNullPage, 0}; } - __asan_report(p, n, "verify", f.kind); } -bool __asan_is_valid(const void *p, size_t n) { - return !__asan_check(p, n).kind; +bool __asan_is_valid(const void *p, long n) { + struct AsanFault f; + f = __asan_check(p, n); + return !f.kind; } bool __asan_is_valid_iov(const struct iovec *iov, int iovlen) { @@ -568,85 +491,118 @@ bool __asan_is_valid_strlist(char *const *p) { } } -static const char *__asan_dscribe_heap_poison(long c) { +static const char *__asan_dscribe_heap_poison(signed char c) { switch (c) { case kAsanHeapFree: return "heap double free"; case kAsanStackFree: - return "stack double free"; - case kAsanRelocated: + return "free stack after return"; + case kAsanHeapRelocated: return "free after relocate"; default: return "this corruption"; } } -static const char *__asan_describe_access_poison(char kind) { +wint_t __asan_symbolize_access_poison(signed char kind) { switch (kind) { + case kAsanNullPage: + return L'∅'; + case kAsanProtected: + return L'P'; case kAsanHeapFree: - return "heap use after free"; + return L'F'; + case kAsanHeapRelocated: + return L'R'; + case kAsanAllocaOverrun: + return L'𝑂'; + case kAsanHeapUnderrun: + return L'U'; + case kAsanHeapOverrun: + return L'O'; + case kAsanStackUnscoped: + return L's'; + case kAsanStackOverflow: + return L'!'; + case kAsanGlobalOrder: + return L'I'; case kAsanStackFree: - return "stack use after release"; - case kAsanRelocated: + return L'r'; + case kAsanStackPartial: + return L'p'; + case kAsanStackOverrun: + return L'o'; + case kAsanStackMiddle: + return L'm'; + case kAsanStackUnderrun: + return L'u'; + case kAsanAllocaUnderrun: + return L'𝑈'; + case kAsanUnmapped: + return L'M'; + case kAsanGlobalRedzone: + return L'G'; + case kAsanGlobalGone: + return L'𝐺'; + default: + return L'?'; + } +} + +const char *__asan_describe_access_poison(signed char kind) { + switch (kind) { + case kAsanNullPage: + return "null pointer dereference"; + case kAsanProtected: + return "protected"; + case kAsanHeapFree: + return "heap use after free"; + case kAsanHeapRelocated: return "heap use after relocate"; + case kAsanAllocaOverrun: + return "alloca overflow"; case kAsanHeapUnderrun: return "heap underrun"; case kAsanHeapOverrun: return "heap overrun"; - case kAsanGlobalOverrun: - return "global overrun"; - case kAsanGlobalUnregistered: - return "global unregistered"; + case kAsanStackUnscoped: + return "stack use after scope"; + case kAsanStackOverflow: + return "stack overflow"; + case kAsanGlobalOrder: + return "global init order"; + case kAsanStackFree: + return "stack use after return"; + case kAsanStackPartial: + return "stack partial"; + case kAsanStackOverrun: + return "stack overrun"; + case kAsanStackMiddle: + return "stack middle"; case kAsanStackUnderrun: return "stack underflow"; - case kAsanStackOverrun: - return "stack overflow"; case kAsanAllocaUnderrun: return "alloca underflow"; - case kAsanAllocaOverrun: - return "alloca overflow"; - case kAsanUnscoped: - return "unscoped"; case kAsanUnmapped: return "unmapped"; - case kAsanProtected: - return "protected"; - case kAsanStackGuard: - return "stack overflow"; - case kAsanNullPage: - return "null page access"; + case kAsanGlobalRedzone: + return "global redzone"; + case kAsanGlobalGone: + return "global gone"; default: return "poisoned"; } } -static bool __asan_ansi(void) { - const char *term; - term = weaken(getenv) ? weaken(getenv)("TERM") : NULL; - return !term || __asan_strcmp(term, "dumb") != 0; -} - -static char *__asan_report_start(char *p, bool ansi) { - if (ansi) p = __asan_stpcpy(p, "\r\e[J\e[1;91m"); - p = __asan_stpcpy(p, "asan error"); - if (ansi) p = __asan_stpcpy(p, "\e[0m"); - return __asan_stpcpy(p, ": "); -} - -static wontreturn void __asan_report_invalid_pointer(void *addr) { - char *p; - p = __asan_report_start(__fatalbuf, __asan_ansi()); - p = __asan_stpcpy(p, "invalid pointer 0x"); - p = __asan_hexcpy(p, (intptr_t)addr, 48); - p = __asan_stpcpy(p, " shadow 0x"); - p = __asan_hexcpy(p, (intptr_t)SHADOW(addr), 48); - p = __asan_stpcpy(p, "\r\n"); - __asan_die(__fatalbuf); +nodiscard static __asan_die_f *__asan_report_invalid_pointer(void *addr) { + __printf("\r\n%sasan error%s: this corruption at 0x%p shadow 0x%p\r\n", + !g_isterminalinarticulate ? "\e[J\e[1;91m" : "", + !g_isterminalinarticulate ? "\e[0m" : "", addr, SHADOW(addr)); + return __asan_die(""); } static char *__asan_format_interval(char *p, intptr_t a, intptr_t b) { - p = __asan_hexcpy(p, a, 48); - *p++ = '-'; + p = __asan_hexcpy(p, a, 48), *p++ = '-'; p = __asan_hexcpy(p, b, 48); return p; } @@ -655,39 +611,31 @@ static char *__asan_format_section(char *p, void *p1, void *p2, const char *name, void *addr) { intptr_t a, b; if ((a = (intptr_t)p1) < (b = (intptr_t)p2)) { - p = __asan_format_interval(p, a, b); - *p++ = ' '; - p = __asan_stpcpy(p, name); + p = __asan_format_interval(p, a, b), *p++ = ' '; + p = __stpcpy(p, name); if (a <= (intptr_t)addr && (intptr_t)addr <= b) { - p = __asan_stpcpy(p, " ←address"); + p = __stpcpy(p, " ←address"); } - *p++ = '\r'; - *p++ = '\n'; + *p++ = '\r', *p++ = '\n'; } return p; } -wontreturn void __asan_report(void *addr, int size, const char *message, - char kind) { - bool a; +nodiscard static __asan_die_f *__asan_report(void *addr, int size, + const char *message, + signed char kind) { wint_t c; int i, cc; signed char t; uint64_t x, y, z; char *p, *q, *base; struct MemoryIntervals *m; - a = __asan_ansi(); - p = __asan_report_start(__fatalbuf, a); - p = __asan_stpcpy(p, __asan_describe_access_poison(kind)); - p = __asan_stpcpy(p, " "); - p = __asan_intcpy(p, size); - p = __asan_stpcpy(p, "-byte "); - p = __asan_stpcpy(p, message); - p = __asan_stpcpy(p, " at 0x"); - p = __asan_hexcpy(p, (uintptr_t)addr, 48); - p = __asan_stpcpy(p, " shadow 0x"); - p = __asan_hexcpy(p, (uintptr_t)SHADOW(addr), 48); - *p++ = '\r', *p++ = '\n'; + p = __fatalbuf; + __printf("\r\n%sasan error%s: %s %d-byte %s at 0x%p shadow 0x%p\r\n", + !g_isterminalinarticulate ? "\e[J\e[1;91m" : "", + !g_isterminalinarticulate ? "\e[0m" : "", + __asan_describe_access_poison(kind), size, message, addr, + SHADOW(addr)); if (0 < size && size < 80) { base = (char *)addr - ((80 >> 1) - (size >> 1)); for (i = 0; i < 80; ++i) { @@ -704,20 +652,20 @@ wontreturn void __asan_report(void *addr, int size, const char *message, *p++ = '\r', *p++ = '\n'; for (c = i = 0; i < 80; ++i) { if (!(t = __asan_check(base + i, 1).kind)) { - if (a && c != 32) { - p = __asan_stpcpy(p, "\e[32m"); + if (!g_isterminalinarticulate && c != 32) { + p = __stpcpy(p, "\e[32m"); c = 32; } *p++ = '.'; } else { - if (a && c != 31) { - p = __asan_stpcpy(p, "\e[31m"); + if (!g_isterminalinarticulate && c != 31) { + p = __stpcpy(p, "\e[31m"); c = 31; } - *p++ = "FFRRUOOGUOUOSMP~"[-t & 15]; + p = __asan_utf8cpy(p, __asan_symbolize_access_poison(t)); } } - if (a) p = __asan_stpcpy(p, "\e[39m"); + if (!g_isterminalinarticulate) p = __stpcpy(p, "\e[39m"); *p++ = '\r', *p++ = '\n'; for (i = 0; (intptr_t)(base + i) & 7; ++i) *p++ = ' '; for (; i + 8 <= 80; i += 8) { @@ -725,7 +673,7 @@ wontreturn void __asan_report(void *addr, int size, const char *message, *p++ = '|'; z = ((intptr_t)(base + i) >> 3) + 0x7fff8000; if (__asan_is_mapped(z >> 16)) { - p = __asan_intcpy(p, *(signed char *)z); + p = __intcpy(p, *(signed char *)z); } else { *p++ = '!'; } @@ -736,15 +684,9 @@ wontreturn void __asan_report(void *addr, int size, const char *message, for (; i < 80; ++i) *p++ = ' '; *p++ = '\r', *p++ = '\n'; for (i = 0; i < 80; ++i) { - if (__asan_exists(base + i)) { - c = kCp437[((unsigned char *)base)[i]]; - } else { - c = u'⋅'; - } - z = __asan_encodeutf8(c); - do { - *p++ = z; - } while ((z >>= 8)); + p = __asan_utf8cpy(p, __asan_exists(base + i) + ? kCp437[((unsigned char *)base)[i]] + : L'⋅'); } *p++ = '\r', *p++ = '\n'; } @@ -756,18 +698,32 @@ wontreturn void __asan_report(void *addr, int size, const char *message, y = m->p[i].y; p = __asan_format_interval(p, x << 16, (y << 16) + (FRAMESIZE - 1)); z = (intptr_t)addr >> 16; - if (x <= z && z <= y) p = __asan_stpcpy(p, " ←address"); + if (x <= z && z <= y) p = __stpcpy(p, " ←address"); z = (((intptr_t)addr >> 3) + 0x7fff8000) >> 16; - if (x <= z && z <= y) p = __asan_stpcpy(p, " ←shadow"); + if (x <= z && z <= y) p = __stpcpy(p, " ←shadow"); *p++ = '\r', *p++ = '\n'; } *p = 0; - __asan_die(__fatalbuf); + return __asan_die(__fatalbuf); } -wontreturn void __asan_report_memory_fault(void *addr, int size, - const char *message) { - __asan_report(addr, size, message, __asan_fault(SHADOW(addr), -128).kind); +void __asan_verify(const void *p, size_t n) { + const char *q; + struct AsanFault f; + if (!(f = __asan_check(p, n)).kind) return; + q = UNSHADOW(f.shadow); + if ((uintptr_t)q != ((uintptr_t)p & -8) && (uintptr_t)q - (uintptr_t)p < n) { + n -= (uintptr_t)q - (uintptr_t)p; + p = q; + } + __asan_report(p, n, "verify", f.kind)(); + __asan_unreachable(); +} + +nodiscard __asan_die_f *__asan_report_memory_fault(void *addr, int size, + const char *message) { + return __asan_report(addr, size, message, + __asan_fault(SHADOW(addr), -128).kind); } const void *__asan_morgue_add(void *p) { @@ -789,7 +745,7 @@ static void __asan_morgue_flush(void) { void *p; for (i = 0; i < ARRAYLEN(__asan_morgue.p); ++i) { p = __asan_morgue.p[i]; - if (cmpxchg(__asan_morgue.p + i, p, NULL)) { + if (cmpxchg(__asan_morgue.p + i, p, 0)) { if (weaken(dlfree)) { weaken(dlfree)(p); } @@ -797,14 +753,6 @@ static void __asan_morgue_flush(void) { } } -static size_t __asan_heap_size(size_t n) { - if (n <= 0x7fffffffffff) { - return __asan_roundup2pow(ROUNDUP(n, 8) + 8); - } else { - return -1; - } -} - static size_t __asan_user_size(size_t n) { if (n) { return n; @@ -813,76 +761,169 @@ static size_t __asan_user_size(size_t n) { } } -static size_t __asan_stack_size(void) { - extern char ape_stack_memsz[] __attribute__((__weak__)); - if (ape_stack_memsz) { - return (uintptr_t)ape_stack_memsz; +static size_t __asan_heap_size(size_t n) { + if (n < 0x7fffffff0000) { + n = ROUNDUP(n, alignof(struct AsanExtra)); + return __asan_roundup2pow(n + sizeof(struct AsanExtra)); } else { - return STACKSIZE; + return -1; } } -forceinline void __asan_write48(char *p, uint64_t x) { - uint64_t value, cookie; - cookie = COOKIE; +static void __asan_write48(uint64_t *value, uint64_t x) { + uint64_t cookie; + cookie = 'J' | 'T' << 8; cookie ^= x & 0xffff; - value = (x & 0xffffffffffff) | cookie << 48; - WRITE64BE(p, value); + *value = (x & 0xffffffffffff) | cookie << 48; } -forceinline bool __asan_read48(const char *p, uint64_t *x) { - uint64_t value, cookie; - value = READ64BE(p); +static bool __asan_read48(uint64_t value, uint64_t *x) { + uint64_t cookie; cookie = value >> 48; cookie ^= value & 0xffff; *x = (int64_t)(value << 16) >> 16; - return cookie == COOKIE; + return cookie == ('J' | 'T' << 8); +} + +static void __asan_trace(struct AsanTrace *bt, const struct StackFrame *bp) { + int f1, f2; + size_t i, gi; + intptr_t addr; + struct Garbages *garbage; + garbage = weaken(__garbage); + gi = garbage ? garbage->i : 0; + __asan_memset(bt, 0, sizeof(*bt)); + for (f1 = -1, i = 0; bp && i < ARRAYLEN(bt->p); ++i, bp = bp->next) { + if (f1 != (f2 = ((intptr_t)bp >> 16))) { + if (!__asan_is_mapped(f2)) break; + f1 = f2; + } + if (!__asan_checka(SHADOW(bp), sizeof(*bp) >> 3).kind) { + addr = bp->addr; + if (addr == weakaddr("__gc") && weakaddr("__gc")) { + do --gi; + while ((addr = garbage->p[gi].ret) == weakaddr("__gc")); + } + bt->p[i] = addr; + } else { + break; + } + } } -static void *__asan_allocate(size_t a, size_t n, int underrun, int overrun) { +static void *__asan_allocate(size_t a, size_t n, int underrun, int overrun, + struct AsanTrace *bt) { char *p; size_t c; + struct AsanExtra *e; + n = __asan_user_size(n); if ((p = weaken(dlmemalign)(a, __asan_heap_size(n)))) { - n = __asan_user_size(n); c = weaken(dlmalloc_usable_size)(p); + e = (struct AsanExtra *)(p + c - sizeof(*e)); __asan_unpoison((uintptr_t)p, n); __asan_poison((uintptr_t)p - 16, 16, underrun); /* see dlmalloc design */ __asan_poison((uintptr_t)p + n, c - n, overrun); __asan_memset(p, 0xF9, n); - __asan_write48(p + c - 8, n); + __asan_write48(&e->size, n); + __asan_memcpy(&e->bt, bt, sizeof(*bt)); } return p; } +static struct AsanExtra *__asan_get_extra(void *p, size_t *c) { + int f; + long x, n; + struct AsanExtra *e; + if ((0 < (intptr_t)p && (intptr_t)p < 0x800000000000) && + __asan_is_mapped((f = (intptr_t)p >> 16)) && + (LIKELY(f == (int)(((intptr_t)p - 16) >> 16)) || + __asan_is_mapped(((intptr_t)p - 16) >> 16)) && + (n = weaken(dlmalloc_usable_size)(p)) > sizeof(*e) && + !__builtin_add_overflow((intptr_t)p, n, &x) && x <= 0x800000000000 && + (LIKELY(f == (int)((x - 1) >> 16)) || __asan_is_mapped((x - 1) >> 16)) && + (LIKELY(f == (int)((x = x - sizeof(*e)) >> 16)) || + __asan_is_mapped(x >> 16)) && + !(x & (alignof(struct AsanExtra) - 1))) { + *c = n; + return (struct AsanExtra *)x; + } else { + return 0; + } +} + +size_t __asan_get_heap_size(const void *p) { + size_t n, c; + struct AsanExtra *e; + if ((e = __asan_get_extra(p, &c))) { + if (__asan_read48(e->size, &n)) { + return n; + } else { + return 0; + } + } else { + return 0; + } +} + static size_t __asan_malloc_usable_size(const void *p) { - size_t c, n; - struct AsanFault f; - if (!(f = __asan_check(p, 1)).kind) { - if ((c = weaken(dlmalloc_usable_size)(p)) >= 8) { - if (__asan_read48((char *)p + c - 8, &n) && n <= c) { - return n; - } + size_t n, c; + struct AsanExtra *e; + if ((e = __asan_get_extra(p, &c))) { + if (__asan_read48(e->size, &n)) { + return n; + } else { + __asan_report_invalid_pointer(p)(); + __asan_unreachable(); } - __asan_report_invalid_pointer(p); } else { - __asan_report(p, 1, "heaping", f.kind); + __asan_report_invalid_pointer(p)(); + __asan_unreachable(); + } +} + +int __asan_print_trace(void *p) { + intptr_t x; + size_t c, i, n; + const char *name; + struct AsanExtra *e; + if (!(e = __asan_get_extra(p, &c))) { + __printf(" bad pointer"); + return einval(); + } + if (!__asan_read48(e->size, &n)) { + __printf(" bad cookie"); + return -1; + } + __printf(" %,d used", n); + if (!__asan_is_mapped((((intptr_t)p >> 3) + 0x7fff8000) >> 16)) { + __printf(" (shadow not mapped?!)"); } + for (i = 0; i < ARRAYLEN(e->bt.p) && e->bt.p[i]; ++i) { + __printf("\n%*x %s", 12, e->bt.p[i], + weaken(__get_symbol_by_addr) + ? weaken(__get_symbol_by_addr)(e->bt.p[i]) + : "please STATIC_YOINK(\"__get_symbol_by_addr\")"); + } + return 0; } static void __asan_deallocate(char *p, long kind) { size_t c, n; - if (__asan_is_mapped((intptr_t)p >> 16) && - (((intptr_t)p >> 16) == ((intptr_t)(p - 16) >> 16) || - __asan_is_mapped((intptr_t)(p - 16) >> 16)) && - (c = weaken(dlmalloc_usable_size)(p)) >= 8 && - __asan_read48(p + c - 8, &n) && n <= c) { - __asan_poison((uintptr_t)p, c, kind); - if (c <= FRAMESIZE) { - p = __asan_morgue_add(p); + struct AsanExtra *e; + if ((e = __asan_get_extra(p, &c))) { + if (__asan_read48(e->size, &n)) { + __asan_poison((uintptr_t)p, c, kind); + if (c <= FRAMESIZE) { + p = __asan_morgue_add(p); + } + weaken(dlfree)(p); + } else { + __asan_report_invalid_pointer(p)(); + __asan_unreachable(); } - weaken(dlfree)(p); } else { - __asan_report_invalid_pointer(p); + __asan_report_invalid_pointer(p)(); + __asan_unreachable(); } } @@ -902,61 +943,74 @@ size_t __asan_bulk_free(void *p[], size_t n) { return 0; } -void *__asan_memalign(size_t align, size_t size) { - return __asan_allocate(align, size, kAsanHeapUnderrun, kAsanHeapOverrun); -} - -void *__asan_malloc(size_t size) { - return __asan_memalign(__BIGGEST_ALIGNMENT__, size); -} - -void *__asan_calloc(size_t n, size_t m) { - char *p; - if (__builtin_mul_overflow(n, m, &n)) n = -1; - if ((p = __asan_malloc(n))) __asan_memset(p, 0, n); - return p; -} - -static void *__asan_realloc_nogrow(void *p, size_t n, size_t m) { +static void *__asan_realloc_nogrow(void *p, size_t n, size_t m, + struct AsanTrace *bt) { return 0; } -static void *__asan_realloc_grow(void *p, size_t n, size_t m) { +static void *__asan_realloc_grow(void *p, size_t n, size_t m, + struct AsanTrace *bt) { char *q; - if ((q = __asan_malloc(n))) { + if ((q = __asan_allocate(16, n, kAsanHeapUnderrun, kAsanHeapOverrun, bt))) { __asan_memcpy(q, p, m); - __asan_deallocate(p, kAsanRelocated); + __asan_deallocate(p, kAsanHeapRelocated); } return q; } static void *__asan_realloc_impl(void *p, size_t n, - void *grow(void *, size_t, size_t)) { - char *f; + void *grow(void *, size_t, size_t, + struct AsanTrace *)) { size_t c, m; - if ((c = weaken(dlmalloc_usable_size)(p)) >= 8) { - f = (char *)p + c - 8; - if (__asan_read48(f, &m) && m <= c) { + struct AsanExtra *e; + if ((e = __asan_get_extra(p, &c))) { + if (__asan_read48(e->size, &m)) { if (n <= m) { /* shrink */ __asan_poison((uintptr_t)p + n, m - n, kAsanHeapOverrun); - __asan_write48(f, n); + __asan_write48(&e->size, n); return p; - } else if (n <= c - 8) { /* small growth */ + } else if (n <= c - sizeof(struct AsanExtra)) { /* small growth */ __asan_unpoison((uintptr_t)p + m, n - m); - __asan_write48(f, n); + __asan_write48(&e->size, n); return p; } else { /* exponential growth */ - return grow(p, n, m); + return grow(p, n, m, &e->bt); } } else { - __asan_report_invalid_pointer(p); + __asan_report_invalid_pointer(p)(); + __asan_unreachable(); } } else { - __asan_report_invalid_pointer(p); + __asan_report_invalid_pointer(p)(); + __asan_unreachable(); + } +} + +void *__asan_malloc(size_t size) { + struct AsanTrace bt; + __asan_trace(&bt, __builtin_frame_address(0)); + return __asan_allocate(16, size, kAsanHeapUnderrun, kAsanHeapOverrun, &bt); +} + +void *__asan_memalign(size_t align, size_t size) { + struct AsanTrace bt; + __asan_trace(&bt, __builtin_frame_address(0)); + return __asan_allocate(align, size, kAsanHeapUnderrun, kAsanHeapOverrun, &bt); +} + +void *__asan_calloc(size_t n, size_t m) { + char *p; + struct AsanTrace bt; + __asan_trace(&bt, __builtin_frame_address(0)); + if (__builtin_mul_overflow(n, m, &n)) n = -1; + if ((p = __asan_allocate(16, n, kAsanHeapUnderrun, kAsanHeapOverrun, &bt))) { + __asan_memset(p, 0, n); } + return p; } void *__asan_realloc(void *p, size_t n) { + struct AsanTrace bt; if (p) { if (n) { return __asan_realloc_impl(p, n, __asan_realloc_grow); @@ -965,7 +1019,8 @@ void *__asan_realloc(void *p, size_t n) { return 0; } } else { - return __asan_malloc(n); + __asan_trace(&bt, __builtin_frame_address(0)); + return __asan_allocate(16, n, kAsanHeapUnderrun, kAsanHeapOverrun, &bt); } } @@ -977,14 +1032,6 @@ void *__asan_realloc_in_place(void *p, size_t n) { } } -void *__asan_valloc(size_t n) { - return __asan_memalign(PAGESIZE, n); -} - -void *__asan_pvalloc(size_t n) { - return __asan_valloc(ROUNDUP(n, PAGESIZE)); -} - int __asan_malloc_trim(size_t pad) { __asan_morgue_flush(); if (weaken(dlmalloc_trim)) { @@ -995,7 +1042,9 @@ int __asan_malloc_trim(size_t pad) { } void *__asan_stack_malloc(size_t size, int classid) { - return __asan_allocate(32, size, kAsanStackUnderrun, kAsanStackOverrun); + struct AsanTrace bt; + __asan_trace(&bt, __builtin_frame_address(0)); + return __asan_allocate(16, size, kAsanStackUnderrun, kAsanStackOverrun, &bt); } void __asan_stack_free(char *p, size_t size, int classid) { @@ -1005,32 +1054,46 @@ void __asan_stack_free(char *p, size_t size, int classid) { void __asan_handle_no_return(void) { uintptr_t stk, ssz; stk = (uintptr_t)__builtin_frame_address(0); - ssz = __asan_stack_size(); + ssz = GetStackSize(); __asan_unpoison(stk, ROUNDUP(stk, ssz) - stk); } void __asan_register_globals(struct AsanGlobal g[], int n) { int i; + __asan_poison((intptr_t)g, sizeof(*g) * n, kAsanProtected); for (i = 0; i < n; ++i) { - __asan_unpoison(g[i].addr, g[i].size); __asan_poison(g[i].addr + g[i].size, g[i].size_with_redzone - g[i].size, - kAsanGlobalOverrun); + kAsanGlobalRedzone); + if (g[i].location) { + __asan_poison((intptr_t)g[i].location, sizeof(*g[i].location), + kAsanProtected); + } } } void __asan_unregister_globals(struct AsanGlobal g[], int n) { int i; for (i = 0; i < n; ++i) { - __asan_poison(g[i].addr, g[i].size_with_redzone, kAsanGlobalUnregistered); + __asan_poison(g[i].addr, g[i].size, kAsanGlobalGone); } } void __asan_report_load(uint8_t *addr, int size) { - __asan_report_memory_fault(addr, size, "load"); + if (cmpxchg(&__asan_noreentry, false, true)) { + __asan_report_memory_fault(addr, size, "load")(); + __asan_unreachable(); + } else { + __printf("WARNING: ASAN error reporting had an ASAN error\r\n"); + } } void __asan_report_store(uint8_t *addr, int size) { - __asan_report_memory_fault(addr, size, "store"); + if (cmpxchg(&__asan_noreentry, false, true)) { + __asan_report_memory_fault(addr, size, "store")(); + __asan_unreachable(); + } else { + __printf("WARNING: ASAN error reporting had an ASAN error\r\n"); + } } void __asan_poison_stack_memory(uintptr_t addr, size_t size) { @@ -1054,19 +1117,17 @@ void __asan_allocas_unpoison(uintptr_t x, uintptr_t y) { void *__asan_addr_is_in_fake_stack(void *fakestack, void *addr, void **beg, void **end) { - return NULL; + return 0; } void *__asan_get_current_fake_stack(void) { - return NULL; + return 0; } void __asan_install_malloc_hooks(void) { HOOK(hook_free, __asan_free); HOOK(hook_malloc, __asan_malloc); HOOK(hook_calloc, __asan_calloc); - HOOK(hook_valloc, __asan_valloc); - HOOK(hook_pvalloc, __asan_pvalloc); HOOK(hook_realloc, __asan_realloc); HOOK(hook_memalign, __asan_memalign); HOOK(hook_bulk_free, __asan_bulk_free); @@ -1076,36 +1137,47 @@ void __asan_install_malloc_hooks(void) { } void __asan_map_shadow(uintptr_t p, size_t n) { + void *addr; + size_t size; + int prot, flag; int i, x, a, b; struct DirectMap sm; struct MemoryIntervals *m; - if ((0x7fff8000 <= p && p < 0x100080000000) || - (0x7fff8000 <= p + n && p + n < 0x100080000000) || - (p < 0x7fff8000 && 0x100080000000 <= p + n)) { - __asan_die("asan error: mmap can't shadow a shadow\r\n"); - } + SYSDEBUG("__asan_map_shadow(0x%p, 0x%x)", p, n); + assert(!OverlapsShadowSpace((void *)p, n)); m = weaken(_mmi); - a = (uintptr_t)SHADOW(p) >> 16; - b = ROUNDUP((uintptr_t)SHADOW(ROUNDUP((uintptr_t)p + n, 8)), 1 << 16) >> 16; - for (; a < b; ++a) { - if (!__asan_is_mapped(a)) { - sm = weaken(sys_mmap)( - (void *)((uintptr_t)a << 16), 1 << 16, PROT_READ | PROT_WRITE, - MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED, -1, 0); - if (sm.addr == MAP_FAILED || - weaken(TrackMemoryInterval)( - m, a, a, sm.maphandle, PROT_READ | PROT_WRITE, - MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED) == -1) { - __asan_die("error: could not map asan shadow memory\n"); + a = (0x7fff8000 + (p >> 3)) >> 16; + b = (0x7fff8000 + (p >> 3) + (n >> 3) + 0xffff) >> 16; + for (; a <= b; a += i) { + i = 1; + if (__asan_is_mapped(a)) { + continue; + } + for (; a + i <= b; ++i) { + if (__asan_is_mapped(a + i)) { + break; } - __asan_repstosb((void *)((uintptr_t)a << 16), kAsanUnmapped, 1 << 16); } + size = (size_t)i << 16; + addr = (void *)(intptr_t)((int64_t)((uint64_t)a << 32) >> 16); + prot = PROT_READ | PROT_WRITE; + flag = MAP_PRIVATE | MAP_FIXED | *weaken(MAP_ANONYMOUS); + sm = weaken(sys_mmap)(addr, size, prot, flag, -1, 0); + if (sm.addr == MAP_FAILED || + weaken(TrackMemoryInterval)( + m, a, a + i - 1, sm.maphandle, PROT_READ | PROT_WRITE, + MAP_PRIVATE | *weaken(MAP_ANONYMOUS) | MAP_FIXED) == -1) { + __asan_die("error: could not map asan shadow memory\n")(); + __asan_unreachable(); + } + __repstosb((void *)(intptr_t)((int64_t)((uint64_t)a << 32) >> 16), + kAsanUnmapped, size); } __asan_unpoison((uintptr_t)p, n); } static textstartup void __asan_shadow_string(char *s) { - __asan_map_shadow((uintptr_t)s, __asan_strlen(s) + 1); + __asan_map_shadow((uintptr_t)s, __strlen(s) + 1); } static textstartup void __asan_shadow_auxv(intptr_t *auxv) { @@ -1132,17 +1204,13 @@ static textstartup void __asan_shadow_string_list(char **list) { static textstartup void __asan_shadow_existing_mappings(void) { size_t i; - uintptr_t rsp, stk, ssz; struct MemoryIntervals m; __asan_memcpy(&m, weaken(_mmi), sizeof(m)); for (i = 0; i < m.i; ++i) { __asan_map_shadow((uintptr_t)m.p[i].x << 16, (uintptr_t)(m.p[i].y - m.p[i].x + 1) << 16); } - rsp = (uintptr_t)__builtin_frame_address(0); - ssz = __asan_stack_size(); - stk = ROUNDDOWN(rsp, ssz); - __asan_poison(stk, PAGESIZE, kAsanStackGuard); + __asan_poison(GetStackAddr(0), PAGESIZE, kAsanStackOverflow); } static textstartup bool IsMemoryManagementRuntimeLinked(void) { @@ -1155,16 +1223,15 @@ textstartup void __asan_init(int argc, char **argv, char **envp, static bool once; if (!cmpxchg(&once, false, true)) return; if (IsWindows() && NtGetVersion() < kNtVersionWindows10) { - __asan_write_string("error: asan binaries require windows10\n"); - __asan_exit(0); /* So `make MODE=dbg test` passes w/ Windows7 */ + __write_str("error: asan binaries require windows10\n"); + _Exit(0); /* So `make MODE=dbg test` passes w/ Windows7 */ } REQUIRE(_mmi); REQUIRE(sys_mmap); REQUIRE(MAP_ANONYMOUS); REQUIRE(TrackMemoryInterval); if (weaken(hook_malloc) || weaken(hook_calloc) || weaken(hook_realloc) || - weaken(hook_realloc_in_place) || weaken(hook_pvalloc) || - weaken(hook_valloc) || weaken(hook_free) || + weaken(hook_realloc_in_place) || weaken(hook_free) || weaken(hook_malloc_usable_size)) { REQUIRE(dlmemalign); REQUIRE(dlmalloc_usable_size); @@ -1173,6 +1240,9 @@ textstartup void __asan_init(int argc, char **argv, char **envp, __asan_map_shadow((uintptr_t)_base, _end - _base); __asan_map_shadow(0, 4096); __asan_poison(0, PAGESIZE, kAsanNullPage); + if (!IsWindows()) { + __sysv_mprotect((void *)0x00007fff8000, 0x10000, PROT_READ); + } __asan_shadow_string_list(argv); __asan_shadow_string_list(envp); __asan_shadow_auxv(auxv); @@ -1185,4 +1255,6 @@ static textstartup void __asan_ctor(void) { } } -const void *const g_asan_ctor[] initarray = {__asan_ctor}; +const void *const g_asan_ctor[] initarray = { + __asan_ctor, +}; diff --git a/libc/intrin/asan.internal.h b/libc/intrin/asan.internal.h index cbe5babfc98..393c7205aee 100644 --- a/libc/intrin/asan.internal.h +++ b/libc/intrin/asan.internal.h @@ -1,53 +1,61 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_ASAN_H_ #define COSMOPOLITAN_LIBC_INTRIN_ASAN_H_ #include "libc/calls/struct/iovec.h" +#include "libc/macros.internal.h" -#define kAsanScale 3 -#define kAsanMagic 0x7fff8000 -#define kAsanHeapFree -1 /* F */ -#define kAsanStackFree -2 /* F */ -#define kAsanRelocated -3 /* R */ -#define kAsanHeapUnderrun -4 /* U */ -#define kAsanHeapOverrun -5 /* O */ -#define kAsanGlobalOverrun -6 /* O */ -#define kAsanGlobalUnregistered -7 /* G */ -#define kAsanStackUnderrun -8 /* U */ -#define kAsanStackOverrun -9 /* O */ -#define kAsanAllocaUnderrun -10 /* U */ -#define kAsanAllocaOverrun -11 /* O */ -#define kAsanUnscoped -12 /* S */ -#define kAsanUnmapped -13 /* M */ -#define kAsanProtected -14 /* P */ -#define kAsanStackGuard -15 /* _ */ -#define kAsanNullPage -16 - -#define SHADOW(x) ((signed char *)(((uintptr_t)(x) >> kAsanScale) + kAsanMagic)) -#define UNSHADOW(x) ((void *)(((uintptr_t)(x) + 0x7fff8000) << 3)) +#define kAsanScale 3 +#define kAsanMagic 0x7fff8000 +#define kAsanNullPage -1 /* ∅ 0xff */ +#define kAsanProtected -2 /* P 0xfe */ +#define kAsanHeapFree -3 /* F 0xfd */ +#define kAsanHeapRelocated -4 /* R 0xfc */ +#define kAsanAllocaOverrun -5 /* 𝑂 0xfb */ +#define kAsanHeapUnderrun -6 /* U 0xfa */ +#define kAsanHeapOverrun -7 /* O 0xf9 */ +#define kAsanStackUnscoped -8 /* s 0xf8 */ +#define kAsanStackOverflow -9 /* ! 0xf7 */ +#define kAsanGlobalOrder -10 /* I 0xf6 */ +#define kAsanStackFree -11 /* r 0xf5 */ +#define kAsanStackPartial -12 /* p 0xf4 */ +#define kAsanStackOverrun -13 /* o 0xf3 */ +#define kAsanStackMiddle -14 /* m 0xf2 */ +#define kAsanStackUnderrun -15 /* u 0xf1 */ +#define kAsanAllocaUnderrun -16 /* 𝑈 0xf0 */ +#define kAsanUnmapped -17 /* M 0xef */ +#define kAsanGlobalRedzone -18 /* G 0xee */ +#define kAsanGlobalGone -19 /* 𝐺 0xed */ + +#define SHADOW(x) ((signed char *)(((intptr_t)(x) >> kAsanScale) + kAsanMagic)) +#define UNSHADOW(x) ((void *)(MAX(0, (intptr_t)(x)-kAsanMagic) << kAsanScale)) + +typedef void __asan_die_f(void); struct AsanFault { - char kind; + signed char kind; signed char *shadow; }; -void __asan_unpoison(uintptr_t, size_t); +extern bool __asan_noreentry; + +void __asan_unpoison(long, long); +void __asan_poison(long, long, signed char); void __asan_verify(const void *, size_t); void __asan_map_shadow(uintptr_t, size_t); -void __asan_poison(uintptr_t, size_t, int); -bool __asan_is_valid(const void *, size_t); -bool __asan_is_valid_strlist(char *const *); -bool __asan_is_valid_iov(const struct iovec *, int); -struct AsanFault __asan_check(const void *, size_t); -void __asan_report_memory_fault(void *, int, const char *) wontreturn; -void __asan_report(void *, int, const char *, char) wontreturn; -void *__asan_memalign(size_t, size_t); +bool __asan_is_valid(const void *, long) nosideeffect; +bool __asan_is_valid_strlist(char *const *) strlenesque; +bool __asan_is_valid_iov(const struct iovec *, int) nosideeffect; +wint_t __asan_symbolize_access_poison(signed char) pureconst; +const char *__asan_describe_access_poison(signed char) pureconst; +struct AsanFault __asan_check(const void *, long) nosideeffect; + void __asan_free(void *); void *__asan_malloc(size_t); +int __asan_malloc_trim(size_t); +int __asan_print_trace(void *); void *__asan_calloc(size_t, size_t); void *__asan_realloc(void *, size_t); +void *__asan_memalign(size_t, size_t); +size_t __asan_get_heap_size(const void *); void *__asan_realloc_in_place(void *, size_t); -void *__asan_valloc(size_t); -void *__asan_pvalloc(size_t); -int __asan_malloc_trim(size_t); -void __asan_die(const char *) wontreturn; #endif /* COSMOPOLITAN_LIBC_INTRIN_ASAN_H_ */ diff --git a/libc/runtime/atexit.c b/libc/intrin/atexit.c similarity index 100% rename from libc/runtime/atexit.c rename to libc/intrin/atexit.c diff --git a/libc/intrin/bzero.c b/libc/intrin/bzero.c index 1ff810d76f8..b7a59105bf9 100644 --- a/libc/intrin/bzero.c +++ b/libc/intrin/bzero.c @@ -28,7 +28,7 @@ typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); noasan static noinline antiquity void bzero_sse(char *p, size_t n) { xmm_t v = {0}; - if (IsAsan()) __asan_check(p, n); + if (IsAsan()) __asan_verify(p, n); if (n <= 32) { *(xmm_t *)(p + n - 16) = v; *(xmm_t *)p = v; @@ -45,7 +45,7 @@ noasan static noinline antiquity void bzero_sse(char *p, size_t n) { noasan microarchitecture("avx") static void bzero_avx(char *p, size_t n) { xmm_t v = {0}; - if (IsAsan()) __asan_check(p, n); + if (IsAsan()) __asan_verify(p, n); if (n <= 32) { *(xmm_t *)(p + n - 16) = v; *(xmm_t *)p = v; @@ -134,11 +134,6 @@ void(bzero)(void *p, size_t n) { char *b; uint64_t x; b = p; - if (IsTiny()) { - if (IsAsan()) __asan_check(p, n); - asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "0"(p), "a"(0)); - return; - } asm("xorl\t%k0,%k0" : "=r"(x)); if (n <= 16) { if (n >= 8) { @@ -153,6 +148,9 @@ void(bzero)(void *p, size_t n) { b[--n] = x; } while (n); } + } else if (IsTiny()) { + asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "0"(p), "a"(0)); + return; } else if (X86_HAVE(AVX)) { bzero_avx(b, n); } else { diff --git a/libc/runtime/cxaatexit.c b/libc/intrin/cxaatexit.c similarity index 70% rename from libc/runtime/cxaatexit.c rename to libc/intrin/cxaatexit.c index 688c6f3119b..4a59186252d 100644 --- a/libc/runtime/cxaatexit.c +++ b/libc/intrin/cxaatexit.c @@ -20,22 +20,12 @@ #include "libc/bits/weaken.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" -#include "libc/nexgen32e/bsf.h" #include "libc/nexgen32e/bsr.h" +#include "libc/runtime/cxaatexit.internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/errfuns.h" -static struct CxaAtexitBlocks { - struct CxaAtexitBlock { - unsigned mask; - struct CxaAtexitBlock *next; - struct CxaAtexit { - void *fp; - void *arg; - void *pred; - } p[ATEXIT_MAX]; - } * p, root; -} __cxa_blocks; +STATIC_YOINK("__cxa_finalize"); /** * Adds global destructor. @@ -52,6 +42,7 @@ static struct CxaAtexitBlocks { * @note folks have forked libc in past just to unbloat atexit() */ noasan int __cxa_atexit(void *fp, void *arg, void *pred) { + /* asan runtime depends on this function */ unsigned i; struct CxaAtexitBlock *b, *b2; _Static_assert(ATEXIT_MAX == CHAR_BIT * sizeof(b->mask), ""); @@ -74,51 +65,3 @@ noasan int __cxa_atexit(void *fp, void *arg, void *pred) { b->p[i].pred = pred; return 0; } - -/** - * Triggers global destructors. - * - * They're called in LIFO order. If a destructor adds more destructors, - * then those destructors will be called immediately following, before - * iteration continues. - * - * @param pred can be null to match all - */ -void __cxa_finalize(void *pred) { - unsigned i, mask; - struct CxaAtexitBlock *b, *b2; -StartOver: - if ((b = __cxa_blocks.p)) { - for (;;) { - mask = b->mask; - while (mask) { - i = bsf(mask); - mask &= ~(1u << i); - if (!pred || pred == b->p[i].pred) { - b->mask &= ~(1u << i); - if (b->p[i].fp) { - ((void (*)(void *))b->p[i].fp)(b->p[i].arg); - goto StartOver; - } - } - } - if (!pred) { - b2 = b->next; - if (b2) { - assert(b != &__cxa_blocks.root); - if (weaken(free)) { - weaken(free)(b); - } - } - __cxa_blocks.p = b2; - goto StartOver; - } else { - if (b->next) { - b = b->next; - } else { - break; - } - } - } - } -} diff --git a/libc/mem/hook/pvalloc.S b/libc/intrin/cxablocks.c similarity index 77% rename from libc/mem/hook/pvalloc.S rename to libc/intrin/cxablocks.c index 1b0f0acc4a5..df3a8dc7ec9 100644 --- a/libc/mem/hook/pvalloc.S +++ b/libc/intrin/cxablocks.c @@ -1,7 +1,7 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,18 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.internal.h" -.source __FILE__ +#include "libc/runtime/cxaatexit.internal.h" - .initbss 202,_init_pvalloc -hook_pvalloc: - .quad 0 - .endobj hook_pvalloc,globl,hidden - .previous - - .init.start 202,_init_pvalloc - .hidden dlpvalloc - ezlea dlpvalloc,ax - stosq - yoink free - .init.end 202,_init_pvalloc +struct CxaAtexitBlocks __cxa_blocks; diff --git a/libc/intrin/cxafinalize.c b/libc/intrin/cxafinalize.c new file mode 100644 index 00000000000..741fee0dc99 --- /dev/null +++ b/libc/intrin/cxafinalize.c @@ -0,0 +1,72 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/bits/weaken.h" +#include "libc/mem/mem.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/runtime/cxaatexit.internal.h" +#include "libc/runtime/runtime.h" + +/** + * Triggers global destructors. + * + * They're called in LIFO order. If a destructor adds more destructors, + * then those destructors will be called immediately following, before + * iteration continues. + * + * @param pred can be null to match all + */ +void __cxa_finalize(void *pred) { + unsigned i, mask; + struct CxaAtexitBlock *b, *b2; +StartOver: + if ((b = __cxa_blocks.p)) { + for (;;) { + mask = b->mask; + while (mask) { + i = bsf(mask); + mask &= ~(1u << i); + if (!pred || pred == b->p[i].pred) { + b->mask &= ~(1u << i); + if (b->p[i].fp) { + ((void (*)(void *))b->p[i].fp)(b->p[i].arg); + goto StartOver; + } + } + } + if (!pred) { + b2 = b->next; + if (b2) { + assert(b != &__cxa_blocks.root); + if (weaken(free)) { + weaken(free)(b); + } + } + __cxa_blocks.p = b2; + goto StartOver; + } else { + if (b->next) { + b = b->next; + } else { + break; + } + } + } + } +} diff --git a/libc/runtime/exit3.c b/libc/intrin/exit.c similarity index 97% rename from libc/runtime/exit3.c rename to libc/intrin/exit.c index 02d0e0ada55..9183d7487f4 100644 --- a/libc/runtime/exit3.c +++ b/libc/intrin/exit.c @@ -36,7 +36,7 @@ extern void(__msabi* __imp_ExitProcess)(uint32_t); * @vforksafe * @noreturn */ -privileged wontreturn void _Exit(int exitcode) { +privileged noinstrument noasan noubsan wontreturn void _Exit(int exitcode) { if ((!IsWindows() && !IsMetal()) || (IsMetal() && IsGenuineCosmo())) { asm volatile("syscall" : /* no outputs */ diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 6ab42708fc9..2686b4db438 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -41,12 +41,27 @@ $(LIBC_INTRIN_A).pkg: \ $(LIBC_INTRIN_A_OBJS) \ $(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x)_A).pkg) +$(LIBC_INTRIN_A_OBJS): \ + OVERRIDE_CFLAGS += \ + -foptimize-sibling-calls + o/$(MODE)/libc/intrin/asan.o \ o/$(MODE)/libc/intrin/ubsan.o: \ OVERRIDE_CFLAGS += \ -fno-sanitize=all \ - -fno-stack-protector \ - -O3 + -fno-stack-protector + +o/$(MODE)/libc/intrin/asan.o: \ + OVERRIDE_CFLAGS += \ + -O2 \ + -finline \ + -finline-functions + +o/$(MODE)/libc/intrin/asan.o \ +o/$(MODE)/libc/intrin/ubsan.o: \ + OVERRIDE_CFLAGS += \ + -fno-sanitize=all \ + -fno-stack-protector o/$(MODE)/libc/intrin/memcmp.o: \ OVERRIDE_CFLAGS += \ @@ -63,12 +78,18 @@ o//libc/intrin/memmove.o: \ OVERRIDE_CFLAGS += \ -O3 -o/tiny/libc/intrin/memcmp.o \ -o/tiny/libc/intrin/memmove.o \ -o/tiny/libc/intrin/memmove-gcc.asm: \ +o/$(MODE)/libc/intrin/bzero.o \ +o/$(MODE)/libc/intrin/memcmp.o \ +o/$(MODE)/libc/intrin/memmove.o: \ OVERRIDE_CFLAGS += \ -fpie +o/$(MODE)/libc/intrin/printf.o: \ + OVERRIDE_CFLAGS += \ + -Os \ + -fpie \ + -mgeneral-regs-only + LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x))) LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS)) LIBC_INTRIN_SRCS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_SRCS)) diff --git a/libc/calls/isdebuggerpresent.c b/libc/intrin/isdebuggerpresent.c similarity index 71% rename from libc/calls/isdebuggerpresent.c rename to libc/intrin/isdebuggerpresent.c index 6847fd73c07..77223646960 100644 --- a/libc/calls/isdebuggerpresent.c +++ b/libc/intrin/isdebuggerpresent.c @@ -16,51 +16,43 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/alg.h" -#include "libc/bits/safemacros.internal.h" -#include "libc/calls/calls.h" -#include "libc/calls/internal.h" #include "libc/dce.h" -#include "libc/fmt/conv.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/nexgen32e/vendor.internal.h" #include "libc/nt/struct/teb.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" -#define kBufSize 1024 -#define kProcStatus "/proc/self/status" -#define kPid "TracerPid:\t" - -static noasan int NtBeingDebugged(void) { - return NtGetPeb()->BeingDebugged; -} +#define kBufSize 1024 +#define kPid "TracerPid:\t" /** * Determines if gdb, strace, windbg, etc. is controlling process. * @return non-zero if attached, otherwise 0 */ -int IsDebuggerPresent(bool force) { +noasan noubsan int IsDebuggerPresent(bool force) { + /* asan runtime depends on this function */ int fd, res; ssize_t got; - char buf[1024]; - res = false; + char *p, buf[1024]; if (!force) { - if (getenv("HEISENDEBUG")) return false; - if (IsGenuineCosmo()) return false; + if (IsGenuineCosmo()) return 0; + if (__getenv(__envp, "HEISENDEBUG")) return 0; } if (IsWindows()) { - res = NtBeingDebugged(); - } else if (IsLinux()) { - if ((fd = sys_openat(AT_FDCWD, kProcStatus, O_RDONLY, 0)) != -1) { - if ((got = sys_read(fd, buf, sizeof(buf) - sizeof(kPid))) != -1) { + return NtGetPeb()->BeingDebugged; /* needs noasan */ + } else { + res = 0; + if ((fd = __sysv_open("/proc/self/status", O_RDONLY, 0)) >= 0) { + if ((got = __sysv_read(fd, buf, sizeof(buf) - 1)) > 0) { buf[got] = '\0'; - res = atoi(firstnonnull(strstr(buf, kPid), kPid) + strlen(kPid)); + if ((p = __strstr(buf, kPid))) { + p += sizeof(kPid) - 1; + res = __atoul(p); + } } - sys_close(fd); + __sysv_close(fd); } + return res; } - return res; } diff --git a/libc/calls/isrunningundermake.c b/libc/intrin/isrunningundermake.c similarity index 88% rename from libc/calls/isrunningundermake.c rename to libc/intrin/isrunningundermake.c index 5628d84839d..9d94b59a22b 100644 --- a/libc/calls/isrunningundermake.c +++ b/libc/intrin/isrunningundermake.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" @@ -28,8 +30,9 @@ bool IsRunningUnderMake(void) { return g_isrunningundermake; } -textstartup void g_isrunningundermake_init(void) { - g_isrunningundermake = !!getenv("MAKEFLAGS"); +textstartup void g_isrunningundermake_init(int argc, char **argv, char **envp, + intptr_t *auxv) { + g_isrunningundermake = !!__getenv(envp, "MAKEFLAGS"); } const void *const g_isrunningundermake_ctor[] initarray = { diff --git a/libc/intrin/isterminalinarticulate.c b/libc/intrin/isterminalinarticulate.c new file mode 100644 index 00000000000..9ee478299b5 --- /dev/null +++ b/libc/intrin/isterminalinarticulate.c @@ -0,0 +1,46 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/safemacros.internal.h" +#include "libc/log/internal.h" +#include "libc/log/libfatal.internal.h" +#include "libc/log/log.h" +#include "libc/nt/enum/version.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" + +bool g_isterminalinarticulate; + +bool IsTerminalInarticulate(void) { + return g_isterminalinarticulate; +} + +textstartup noasan void g_isterminalinarticulate_init(int argc, char **argv, + char **envp, + intptr_t *auxv) { + char *s; + if (IsWindows() && NtGetVersion() < kNtVersionWindows10) { + g_isterminalinarticulate = true; + } else if ((s = __getenv(envp, "TERM"))) { + g_isterminalinarticulate = !__strcmp(s, "dumb"); + } +} + +const void *const g_isterminalinarticulate_ctor[] initarray = { + g_isterminalinarticulate_init, +}; diff --git a/libc/intrin/memcmp.c b/libc/intrin/memcmp.c index d96ba38a275..ceb73d077de 100644 --- a/libc/intrin/memcmp.c +++ b/libc/intrin/memcmp.c @@ -21,6 +21,8 @@ #include "libc/nexgen32e/x86feature.h" #include "libc/str/str.h" +#define PMOVMSKB(x) __builtin_ia32_pmovmskb128(x) + typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1))); static noinline antiquity int memcmp_sse(const unsigned char *p, @@ -29,9 +31,7 @@ static noinline antiquity int memcmp_sse(const unsigned char *p, unsigned u, u0, u1, u2, u3; if (n > 32) { while (n > 16 + 16) { - if (!(u = __builtin_ia32_pmovmskb128(*(const xmm_t *)p == - *(const xmm_t *)q) - - 0xffff)) { + if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) { n -= 16; p += 16; q += 16; @@ -41,10 +41,8 @@ static noinline antiquity int memcmp_sse(const unsigned char *p, } } } - if (!(u = __builtin_ia32_pmovmskb128(*(const xmm_t *)p == *(const xmm_t *)q) - - 0xffff)) { - if (!(u = __builtin_ia32_pmovmskb128(*(const xmm_t *)(p + n - 16) == - *(const xmm_t *)(q + n - 16)) - + if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) { + if (!(u = PMOVMSKB(*(xmm_t *)(p + n - 16) == *(xmm_t *)(q + n - 16)) ^ 0xffff)) { return 0; } else { @@ -61,19 +59,13 @@ microarchitecture("avx") static int memcmp_avx(const unsigned char *p, const unsigned char *q, size_t n) { uint64_t w; - unsigned u, u0, u1, u2, u3; + unsigned u; if (n > 32) { while (n >= 16 + 64) { - u0 = __builtin_ia32_pmovmskb128( - (((const xmm_t *)p)[0] == ((const xmm_t *)q)[0])); - u1 = __builtin_ia32_pmovmskb128( - (((const xmm_t *)p)[1] == ((const xmm_t *)q)[1])); - u2 = __builtin_ia32_pmovmskb128( - (((const xmm_t *)p)[2] == ((const xmm_t *)q)[2])); - u3 = __builtin_ia32_pmovmskb128( - (((const xmm_t *)p)[3] == ((const xmm_t *)q)[3])); - w = (uint64_t)u0 | (uint64_t)u1 << 16 | (uint64_t)u2 << 32 | - (uint64_t)u3 << 48; + w = (uint64_t)PMOVMSKB(((xmm_t *)p)[0] == ((xmm_t *)q)[0]) << 000 | + (uint64_t)PMOVMSKB(((xmm_t *)p)[1] == ((xmm_t *)q)[1]) << 020 | + (uint64_t)PMOVMSKB(((xmm_t *)p)[2] == ((xmm_t *)q)[2]) << 040 | + (uint64_t)PMOVMSKB(((xmm_t *)p)[3] == ((xmm_t *)q)[3]) << 060; if (w == -1) { n -= 64; p += 64; @@ -84,9 +76,7 @@ microarchitecture("avx") static int memcmp_avx(const unsigned char *p, } } while (n > 16 + 16) { - if (!(u = __builtin_ia32_pmovmskb128(*(const xmm_t *)p == - *(const xmm_t *)q) - - 0xffff)) { + if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) { n -= 16; p += 16; q += 16; @@ -96,10 +86,8 @@ microarchitecture("avx") static int memcmp_avx(const unsigned char *p, } } } - if (!(u = __builtin_ia32_pmovmskb128(*(const xmm_t *)p == *(const xmm_t *)q) - - 0xffff)) { - if (!(u = __builtin_ia32_pmovmskb128(*(const xmm_t *)(p + n - 16) == - *(const xmm_t *)(q + n - 16)) - + if (!(u = PMOVMSKB(*(xmm_t *)p == *(xmm_t *)q) ^ 0xffff)) { + if (!(u = PMOVMSKB(*(xmm_t *)(p + n - 16) == *(xmm_t *)(q + n - 16)) ^ 0xffff)) { return 0; } else { diff --git a/libc/intrin/memmove.c b/libc/intrin/memmove.c index 131248ed9b7..2ac8564485c 100644 --- a/libc/intrin/memmove.c +++ b/libc/intrin/memmove.c @@ -26,9 +26,6 @@ typedef long long xmm_t __attribute__((__vector_size__(16), __aligned__(1))); typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); -asm("memcpy = memmove\n\t" - ".globl\tmemcpy"); - /** * Copies memory. * @@ -120,8 +117,6 @@ void *memmove(void *dst, const void *src, size_t n) { *d = *s; } } else { - if (IsAsan()) __asan_check(d, n); - if (IsAsan()) __asan_check(s, n); if (d <= s) { asm("rep movsb" : "+D"(d), "+S"(s), "+c"(n), "=m"(*(char(*)[n])dst) @@ -224,8 +219,8 @@ void *memmove(void *dst, const void *src, size_t n) { *(xmm_t *)(d + n + 16) = w; } while (n >= 32); } else { - if (IsAsan()) __asan_check(d, n); - if (IsAsan()) __asan_check(s, n); + if (IsAsan()) __asan_verify(d, n); + if (IsAsan()) __asan_verify(s, n); asm("std\n\t" "rep movsb\n\t" "cld" @@ -246,8 +241,8 @@ void *memmove(void *dst, const void *src, size_t n) { s += i; n -= i; } else { - if (IsAsan()) __asan_check(d, n); - if (IsAsan()) __asan_check(s, n); + if (IsAsan()) __asan_verify(d, n); + if (IsAsan()) __asan_verify(s, n); asm("rep movsb" : "+D"(d), "+S"(s), "+c"(n), "=m"(*(char(*)[n])d) : "m"(*(char(*)[n])s)); @@ -313,3 +308,6 @@ void *memmove(void *dst, const void *src, size_t n) { return dst; } } + +asm("memcpy = memmove\n\t" + ".globl\tmemcpy"); diff --git a/libc/intrin/memset.c b/libc/intrin/memset.c index a7110fe671e..e712273cbd0 100644 --- a/libc/intrin/memset.c +++ b/libc/intrin/memset.c @@ -28,7 +28,7 @@ typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16))); noasan static noinline antiquity void *memset_sse(char *p, char c, size_t n) { xmm_t v = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; - if (IsAsan()) __asan_check(p, n); + if (IsAsan()) __asan_verify(p, n); if (n <= 32) { *(xmm_t *)(p + n - 16) = v; *(xmm_t *)p = v; @@ -48,7 +48,7 @@ noasan microarchitecture("avx") static void *memset_avx(char *p, char c, size_t n) { char *t; xmm_t v = {c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c}; - if (IsAsan()) __asan_check(p, n); + if (IsAsan()) __asan_verify(p, n); if (n <= 32) { *(xmm_t *)(p + n - 16) = v; *(xmm_t *)p = v; @@ -140,11 +140,6 @@ void *memset(void *p, int c, size_t n) { uint32_t u; uint64_t x; b = p; - if (IsTiny()) { - if (IsAsan()) __asan_check(p, n); - asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "0"(p), "a"(c)); - return p; - } if (n <= 16) { if (n >= 8) { x = 0x0101010101010101ul * (c & 255); @@ -161,6 +156,9 @@ void *memset(void *p, int c, size_t n) { } while (n); } return b; + } else if (IsTiny()) { + asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "0"(p), "a"(c)); + return p; } else if (X86_HAVE(AVX)) { return memset_avx(b, c, n); } else { diff --git a/libc/intrin/printf.c b/libc/intrin/printf.c index 10f9f0bade1..8374bd263ac 100644 --- a/libc/intrin/printf.c +++ b/libc/intrin/printf.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/limits.h" #include "libc/log/libfatal.internal.h" -#include "libc/nexgen32e/bsr.h" #include "libc/nexgen32e/uart.internal.h" #include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" @@ -26,23 +25,25 @@ #include "libc/sysv/consts/nr.h" /** - * Low-level printf. + * Privileged printf. * * This will work without any cosmopolitan runtime support once the * executable has been loaded into memory. */ -privileged noasan noinstrument void __printf(const char *fmt, ...) { - long d, ax; +privileged noasan noubsan noinstrument void __printf(const char *fmt, ...) { + /* system call support runtime depends on this function */ + /* function tracing runtime depends on this function */ + /* asan runtime depends on this function */ + short w[2]; va_list va; uint16_t dx; - const char *s; + const void *s; uint32_t wrote; unsigned long x; unsigned char al; - const char16_t *S; - int i, n, t, w, plus; - char c, f, *p, *e, b[2048]; - w = 0; + int i, j, t, cstr; + long d, rax, rdi, rsi, rdx, dot; + char c, *p, *e, pad, bits, base, sign, thou, z[28], b[2048]; p = b; e = p + sizeof(b); va_start(va, fmt); @@ -56,98 +57,132 @@ privileged noasan noinstrument void __printf(const char *fmt, ...) { case '\0': break; case '%': - w = 0; - f = ' '; - plus = 0; - n = INT_MAX; + dot = 0; + pad = ' '; + sign = 0; + bits = 0; + thou = 0; + w[0] = 0; + w[1] = SHRT_MAX; NeedMoar: switch ((c = *fmt++)) { case '\0': break; - case '0': - f = c; + case 'l': + case 'z': goto NeedMoar; + case ' ': case '+': - plus = c; + sign = c; + goto NeedMoar; + case 'e': + dot = 1; + goto NeedMoar; + case ',': + thou = c; + goto NeedMoar; + case 'h': + bits = 16; + goto NeedMoar; + case '0': + pad = c; + /* fallthrough */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + w[dot] *= 10; + w[dot] += c - '0'; goto NeedMoar; case '*': - w = va_arg(va, int); + w[dot] = va_arg(va, int); goto NeedMoar; case 'd': d = va_arg(va, long); ApiAbuse: - if (p + 22 <= e) { - if (d || !plus) { - if (d > 0 && plus) { - *p++ = plus; - } - p = __intcpy(p, d); - } - } - break; - case 'p': - w = 12; - f = '0'; - /* fallthrough */ - case 'x': - x = va_arg(va, unsigned long); - if (x) { - n = __builtin_clzl(x) ^ (sizeof(long) * 8 - 1); - n >>= 2; - n += 1; - } else { - n = 1; + x = d; + if (d < 0) { + x = -x; + sign = '-'; } - while (w-- > n) { - if (p < e) { - *p++ = f; + for (i = j = 0;;) { + z[i++] = x % 10 + '0'; + if (!(x /= 10)) break; + if (thou && !(++j % 3)) { + z[i++] = thou; } } - while (n--) { - if (p < e) { - *p++ = "0123456789abcdef"[(x >> (n << 2)) & 15]; - } + if (sign) { + z[i++] = sign; + } + EmitNumber: + while (w[0]-- > i) { + if (p < e) *p++ = pad; } + do { + if (p < e) *p++ = z[--i]; + } while (i); break; - case 'S': - n = va_arg(va, int); + case 'b': + base = 1; + BinaryNumber: + i = 0; + x = va_arg(va, unsigned long); + do z[i++] = "0123456789abcdef"[x & ((1 << base) - 1)]; + while ((x >>= base) && i < w[1]); + goto EmitNumber; + case 'p': + pad = '0'; + w[0] = 12; + w[1] = 12; /* fallthrough */ + case 'x': + base = 4; + goto BinaryNumber; + case 'o': + base = 3; + goto BinaryNumber; + case 'c': + cstr = va_arg(va, int); + s = &cstr; + goto EmitString; case 's': - s = va_arg(va, const char *); + s = va_arg(va, const void *); + EmitString: if (!s) { - EmitNullString: s = "NULL"; - } - if ((uintptr_t)s < PAGESIZE) { + bits = 0; + } else if ((uintptr_t)s < PAGESIZE) { d = (intptr_t)s; goto ApiAbuse; } - for (i = 0; i < n; ++i) { - if (!s[i]) { - n = i; - break; - } - } - while (w-- > n) { - if (p < e) { - *p++ = f; - } - } - for (i = 0; i < n && p < e; ++i) { - *p++ = s[i]; - } - break; - case 'u': - S = va_arg(va, const char16_t *); - if (!S) goto EmitNullString; - while ((t = *S++)) { - if (p + 3 <= e && (t & 0xfc00) != 0xdc00) { - if (t <= 0x7ff) { - p[0] = 0300 | t >> 6; - p[1] = 0200 | x << 8 | t & 077; - p += 2; + for (i = 0; i < w[1]; ++i) { + if (!bits) { + t = ((const char *)s)[i]; + EmitByte: + if (t) { + if (p < e) { + *p++ = t; + } } else { - if (t > 0xffff) t = 0xfffd; + break; + } + } else { + t = ((const char16_t *)s)[i]; + if (t <= 0x7f) { + goto EmitByte; + } else if (t <= 0x7ff) { + if (p + 1 < e) { + p[0] = 0300 | t >> 6; + p[1] = 0200 | x << 8 | t & 077; + p += 2; + } + } else if (p + 2 < e) { p[0] = 0340 | t >> 12; p[1] = 0200 | x << 8 | (t >> 6) & 077; p[2] = 0200 | x << 8 | t & 077; @@ -155,6 +190,9 @@ privileged noasan noinstrument void __printf(const char *fmt, ...) { } } } + while (w[0]-- > i) { + if (p < e) *p++ = pad; + } break; default: break; @@ -186,8 +224,8 @@ privileged noasan noinstrument void __printf(const char *fmt, ...) { } } else { asm volatile("syscall" - : "=a"(ax) - : "0"(__NR_write), "D"(2), "S"(b), "d"(p - b) - : "rcx", "r11", "memory"); + : "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx) + : "0"(__NR_write), "1"(2L), "2"(b), "3"(p - b) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); } } diff --git a/libc/intrin/printsystemmappings.c b/libc/intrin/printsystemmappings.c new file mode 100644 index 00000000000..b890efb3a45 --- /dev/null +++ b/libc/intrin/printsystemmappings.c @@ -0,0 +1,37 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/log/libfatal.internal.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/sysv/consts/o.h" + +privileged void PrintSystemMappings(int outfd) { + int infd; + ssize_t rc; + char buf[64]; + if (!IsWindows()) { + if ((infd = __sysv_open("/proc/self/maps", O_RDONLY, 0)) >= 0) { + __sysv_write(outfd, "\n", 1); + while ((rc = __sysv_read(infd, buf, sizeof(buf))) > 0) { + __sysv_write(outfd, buf, rc); + } + } + __sysv_close(infd); + } +} diff --git a/libc/intrin/somanyasan.S b/libc/intrin/somanyasan.S index 818314184ec..807d176cc83 100644 --- a/libc/intrin/somanyasan.S +++ b/libc/intrin/somanyasan.S @@ -19,384 +19,165 @@ #include "libc/macros.internal.h" .source __FILE__ - .macro .acall fn:req - xor %eax,%eax - mov $1,%r10b - cmpxchg %r10b,__asan_noreentry(%rip) - jnz 2f - call \fn - decb __asan_noreentry(%rip) -2: nop - .endm - .rodata.cst4 __asan_option_detect_stack_use_after_return: .long 0 .endobj __asan_option_detect_stack_use_after_return,globl .previous - .bss -__asan_noreentry: - .byte 0 - .endobj __asan_noreentry - .previous - __asan_report_load1: - push %rbp - mov %rsp,%rbp - .profilable - mov $1,%esi - .acall __asan_report_load - pop %rbp - ret + push $1 + jmp 1f .endfn __asan_report_load1,globl - __asan_report_load2: - push %rbp - mov %rsp,%rbp - .profilable - mov $2,%esi - .acall __asan_report_load - pop %rbp - ret + push $2 + jmp 1f .endfn __asan_report_load2,globl - __asan_report_load4: - push %rbp - mov %rsp,%rbp - .profilable - mov $4,%esi - .acall __asan_report_load - pop %rbp - ret + push $4 + jmp 1f .endfn __asan_report_load4,globl - __asan_report_load8: - push %rbp - mov %rsp,%rbp - .profilable - mov $8,%esi - .acall __asan_report_load - pop %rbp - ret + push $8 + jmp 1f .endfn __asan_report_load8,globl - __asan_report_load16: - push %rbp - mov %rsp,%rbp - .profilable - mov $16,%esi - .acall __asan_report_load - pop %rbp - ret + push $16 + jmp 1f .endfn __asan_report_load16,globl - __asan_report_load32: - push %rbp - mov %rsp,%rbp - .profilable - mov $32,%esi - .acall __asan_report_load - pop %rbp - ret + push $32 +1: pop %rsi +0: jmp __asan_report_load .endfn __asan_report_load32,globl - __asan_report_load_n: - push %rbp - mov %rsp,%rbp - .profilable - .acall __asan_report_load - pop %rbp - ret + jmp 0b .endfn __asan_report_load_n,globl __asan_report_store1: - push %rbp - mov %rsp,%rbp - .profilable - mov $1,%esi - .acall __asan_report_store - pop %rbp - ret + push $1 + jmp 1f .endfn __asan_report_store1,globl - __asan_report_store2: - push %rbp - mov %rsp,%rbp - .profilable - mov $2,%esi - .acall __asan_report_store - pop %rbp - ret + push $2 + jmp 1f .endfn __asan_report_store2,globl - __asan_report_store4: - push %rbp - mov %rsp,%rbp - .profilable - mov $4,%esi - .acall __asan_report_store - pop %rbp - ret + push $4 + jmp 1f .endfn __asan_report_store4,globl - __asan_report_store8: - push %rbp - mov %rsp,%rbp - .profilable - mov $8,%esi - .acall __asan_report_store - pop %rbp - ret + push $8 + jmp 1f .endfn __asan_report_store8,globl - __asan_report_store16: - push %rbp - mov %rsp,%rbp - .profilable - mov $16,%esi - .acall __asan_report_store - pop %rbp - ret + push $16 + jmp 1f .endfn __asan_report_store16,globl - __asan_report_store32: - push %rbp - mov %rsp,%rbp - .profilable - mov $32,%esi - .acall __asan_report_store - pop %rbp - ret + push $32 +1: pop %rsi +0: jmp __asan_report_store .endfn __asan_report_store32,globl - __asan_report_store_n: - push %rbp - mov %rsp,%rbp - .profilable - .acall __asan_report_store - pop %rbp - ret + jmp 0b .endfn __asan_report_store_n,globl __asan_stack_free_0: - push %rbp - mov %rsp,%rbp - .profilable - mov $0,%edx - call __asan_stack_free - pop %rbp - ret + push $0 + jmp 1f .endfn __asan_stack_free_0,globl - __asan_stack_free_1: - push %rbp - mov %rsp,%rbp - .profilable - mov $1,%edx - call __asan_stack_free - pop %rbp - ret + push $1 + jmp 1f .endfn __asan_stack_free_1,globl - __asan_stack_free_2: - push %rbp - mov %rsp,%rbp - .profilable - mov $2,%edx - call __asan_stack_free - pop %rbp - ret + push $2 + jmp 1f .endfn __asan_stack_free_2,globl - __asan_stack_free_3: - push %rbp - mov %rsp,%rbp - .profilable - mov $3,%edx - call __asan_stack_free - pop %rbp - ret + push $3 + jmp 1f .endfn __asan_stack_free_3,globl - __asan_stack_free_4: - push %rbp - mov %rsp,%rbp - .profilable - mov $4,%edx - call __asan_stack_free - pop %rbp - ret + push $4 + jmp 1f .endfn __asan_stack_free_4,globl - __asan_stack_free_5: - push %rbp - mov %rsp,%rbp - .profilable - mov $5,%edx - call __asan_stack_free - pop %rbp - ret + push $5 .endfn __asan_stack_free_5,globl - +__asan_stack_free_hop: +1: pop %rdx + jmp __asan_stack_free + .endfn __asan_report_store_n,globl __asan_stack_free_6: - push %rbp - mov %rsp,%rbp - .profilable - mov $6,%edx - call __asan_stack_free - pop %rbp - ret + push $6 + jmp 1b .endfn __asan_stack_free_6,globl - __asan_stack_free_7: - push %rbp - mov %rsp,%rbp - .profilable - mov $7,%edx - call __asan_stack_free - pop %rbp - ret + push $7 + jmp 1b .endfn __asan_stack_free_7,globl - __asan_stack_free_8: - push %rbp - mov %rsp,%rbp - .profilable - mov $8,%edx - call __asan_stack_free - pop %rbp - ret + push $8 + jmp 1b .endfn __asan_stack_free_8,globl - __asan_stack_free_9: - push %rbp - mov %rsp,%rbp - .profilable - mov $9,%edx - call __asan_stack_free - pop %rbp - ret + push $9 + jmp 1b .endfn __asan_stack_free_9,globl - __asan_stack_free_10: - push %rbp - mov %rsp,%rbp - .profilable - mov $10,%edx - call __asan_stack_free - pop %rbp - ret + push $10 + jmp 1b .endfn __asan_stack_free_10,globl __asan_stack_malloc_0: - push %rbp - mov %rsp,%rbp - .profilable - mov $0,%esi - call __asan_stack_malloc - pop %rbp - ret + push $0 + jmp 1f .endfn __asan_stack_malloc_0,globl - __asan_stack_malloc_1: - push %rbp - mov %rsp,%rbp - .profilable - mov $1,%esi - call __asan_stack_malloc - pop %rbp - ret + push $1 + jmp 1f .endfn __asan_stack_malloc_1,globl - __asan_stack_malloc_2: - push %rbp - mov %rsp,%rbp - .profilable - mov $2,%esi - call __asan_stack_malloc - pop %rbp - ret + push $2 + jmp 1f .endfn __asan_stack_malloc_2,globl - __asan_stack_malloc_3: - push %rbp - mov %rsp,%rbp - .profilable - mov $3,%esi - call __asan_stack_malloc - pop %rbp - ret + push $3 + jmp 1f .endfn __asan_stack_malloc_3,globl - __asan_stack_malloc_4: - push %rbp - mov %rsp,%rbp - .profilable - mov $4,%esi - call __asan_stack_malloc - pop %rbp - ret + push $4 + jmp 1f .endfn __asan_stack_malloc_4,globl - __asan_stack_malloc_5: - push %rbp - mov %rsp,%rbp - .profilable - mov $5,%esi - call __asan_stack_malloc - pop %rbp - ret + push $5 + jmp 1f .endfn __asan_stack_malloc_5,globl - +__asan_stack_malloc_hop: +1: pop %rsi + jmp __asan_stack_malloc + .endfn __asan_report_store_n,globl __asan_stack_malloc_6: - push %rbp - mov %rsp,%rbp - .profilable - mov $6,%esi - call __asan_stack_malloc - pop %rbp - ret + push $6 + jmp 1b .endfn __asan_stack_malloc_6,globl - __asan_stack_malloc_7: - push %rbp - mov %rsp,%rbp - .profilable - mov $7,%esi - call __asan_stack_malloc - pop %rbp - ret + push $7 + jmp 1b .endfn __asan_stack_malloc_7,globl - __asan_stack_malloc_8: - push %rbp - mov %rsp,%rbp - .profilable - mov $8,%esi - call __asan_stack_malloc - pop %rbp - ret + push $8 + jmp 1b .endfn __asan_stack_malloc_8,globl - __asan_stack_malloc_9: - push %rbp - mov %rsp,%rbp - .profilable - mov $9,%esi - call __asan_stack_malloc - pop %rbp - ret + push $9 + jmp 1b .endfn __asan_stack_malloc_9,globl - __asan_stack_malloc_10: - push %rbp - mov %rsp,%rbp - .profilable - mov $10,%esi - call __asan_stack_malloc - pop %rbp - ret + push $10 + jmp 1b .endfn __asan_stack_malloc_10,globl __asan_version_mismatch_check_v8: diff --git a/libc/intrin/syscall.S b/libc/intrin/syscall.S index 47e2b1f5726..5c0650d0236 100644 --- a/libc/intrin/syscall.S +++ b/libc/intrin/syscall.S @@ -17,8 +17,49 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" +.privileged +// Invokes SYSCALL for libfatal forceinline asm() routines. +// +// @param rax is ordinal +// @param rdi is arg1 +// @param rsi is arg2 +// @param rdx is arg3 +// @param rcx is arg4 +// @param r8 is arg5 +// @param r9 is arg6 +// @param rsp may contain more args +// @return rdx:rax where rax holds -errno on error +// @clob rax,rdx,memory,cc __syscall__: + mov %rcx,.Lrcx(%rip) + mov %rdi,.Lrdi(%rip) + mov %rsi,.Lrsi(%rip) + mov %r8,.Lr8(%rip) + mov %r9,.Lr9(%rip) + mov %r10,.Lr10(%rip) + mov %r11,.Lr11(%rip) + mov %rcx,%r10 + clc syscall + jnc 1f + neg %rax +1: mov .Lrcx(%rip),%rcx + mov .Lrdi(%rip),%rdi + mov .Lrsi(%rip),%rsi + mov .Lr8(%rip),%r8 + mov .Lr9(%rip),%r9 + mov .Lr10(%rip),%r10 + mov .Lr11(%rip),%r11 ret .endfn __syscall__,globl,hidden + + .bss + .align 8 +.Lrcx: .quad 0 # clobbered by syscall +.Lrdi: .quad 0 # just in case +.Lrsi: .quad 0 # just in case +.Lr8: .quad 0 # freebsd bug? +.Lr9: .quad 0 # just in case +.Lr10: .quad 0 # just in case +.Lr11: .quad 0 # clobbered by syscall diff --git a/libc/intrin/ubsan.c b/libc/intrin/ubsan.c index 150133fcad2..ae95fede585 100644 --- a/libc/intrin/ubsan.c +++ b/libc/intrin/ubsan.c @@ -22,6 +22,7 @@ #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" #include "libc/log/internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/nt/runtime.h" #include "libc/runtime/internal.h" @@ -151,55 +152,19 @@ static bool __ubsan_negative(struct UbsanTypeDescriptor *t, uintptr_t x) { return __ubsan_signed(t) && (intptr_t)x < 0; } -static size_t __ubsan_strlen(const char *s) { - size_t n = 0; - while (*s++) ++n; - return n; -} - -static char *__ubsan_stpcpy(char *d, const char *s) { - size_t i; - for (i = 0;; ++i) { - if (!(d[i] = s[i])) { - return d + i; - } - } -} - -static char *__ubsan_poscpy(char *p, uintptr_t i) { - int j = 0; - do { - p[j++] = i % 10 + '0'; - i /= 10; - } while (i > 0); - reverse(p, j); - return p + j; -} - -static char *__ubsan_intcpy(char *p, intptr_t i) { - if (i >= 0) return __ubsan_poscpy(p, i); - *p++ = '-'; - return __ubsan_poscpy(p, -i); -} - -static char *__ubsan_hexcpy(char *p, uintptr_t x, int k) { - while (k) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15]; - return p; -} - static char *__ubsan_itpcpy(char *p, struct UbsanTypeDescriptor *t, uintptr_t x) { if (__ubsan_signed(t)) { - return __ubsan_intcpy(p, x); + return __intcpy(p, x); } else { - return __ubsan_poscpy(p, x); + return __uintcpy(p, x); } } static const char *__ubsan_dubnul(const char *s, unsigned i) { size_t n; while (i--) { - if ((n = __ubsan_strlen(s))) { + if ((n = __strlen(s))) { s += n + 1; } else { return NULL; @@ -222,53 +187,11 @@ static uintptr_t __ubsan_extend(struct UbsanTypeDescriptor *t, uintptr_t x) { return x; } -static privileged noinline wontreturn void __ubsan_exit(int rc) { - if (!IsWindows()) { - asm volatile("syscall" - : /* no outputs */ - : "a"(__NR_exit_group), "D"(rc) - : "memory"); - unreachable; - } else { - ExitProcess(rc); - } -} - -static privileged noinline ssize_t __ubsan_write(const void *data, - size_t size) { - ssize_t rc; - uint32_t wrote; - if (!IsWindows()) { - asm volatile("syscall" - : "=a"(rc) - : "0"(__NR_write), "D"(2), "S"(data), "d"(size) - : "rcx", "r11", "memory"); - return rc; - } else { - if (WriteFile(GetStdHandle(kNtStdErrorHandle), data, size, &wrote, 0)) { - return wrote; - } else { - return -1; - } - } -} - -static ssize_t __ubsan_write_string(const char *s) { - return __ubsan_write(s, __ubsan_strlen(s)); -} - void __ubsan_abort(const struct UbsanSourceLocation *loc, const char *description) { - char buf[1024], *p = buf; - p = __ubsan_stpcpy(p, "error: "); - p = __ubsan_stpcpy(p, loc->file), *p++ = ':'; - p = __ubsan_intcpy(p, loc->line); - p = __ubsan_stpcpy(p, ": "); - p = __ubsan_stpcpy(p, description); - p = __ubsan_stpcpy(p, "\r\n"); - __ubsan_write_string(buf); + __printf("\r\n%s:%d: ubsan error: %s\r\n", loc->file, loc->line, description); if (weaken(__die)) weaken(__die)(); - __ubsan_exit(134); + _Exit(134); } static const char *__ubsan_describe_shift( @@ -291,11 +214,11 @@ void __ubsan_handle_shift_out_of_bounds(struct UbsanShiftOutOfBoundsInfo *info, char buf[512], *p = buf; lhs = __ubsan_extend(info->lhs_type, lhs); rhs = __ubsan_extend(info->rhs_type, rhs); - p = __ubsan_stpcpy(p, __ubsan_describe_shift(info, lhs, rhs)), *p++ = ' '; + p = __stpcpy(p, __ubsan_describe_shift(info, lhs, rhs)), *p++ = ' '; p = __ubsan_itpcpy(p, info->lhs_type, lhs), *p++ = ' '; - p = __ubsan_stpcpy(p, info->lhs_type->name), *p++ = ' '; + p = __stpcpy(p, info->lhs_type->name), *p++ = ' '; p = __ubsan_itpcpy(p, info->rhs_type, rhs), *p++ = ' '; - p = __ubsan_stpcpy(p, info->rhs_type->name); + p = __stpcpy(p, info->rhs_type->name); __ubsan_abort(&info->location, buf); } @@ -307,12 +230,12 @@ void __ubsan_handle_shift_out_of_bounds_abort( void __ubsan_handle_out_of_bounds(struct UbsanOutOfBoundsInfo *info, uintptr_t index) { char buf[512], *p = buf; - p = __ubsan_stpcpy(p, info->index_type->name); - p = __ubsan_stpcpy(p, " index "); + p = __stpcpy(p, info->index_type->name); + p = __stpcpy(p, " index "); p = __ubsan_itpcpy(p, info->index_type, index); - p = __ubsan_stpcpy(p, " into "); - p = __ubsan_stpcpy(p, info->array_type->name); - p = __ubsan_stpcpy(p, " out of bounds"); + p = __stpcpy(p, " into "); + p = __stpcpy(p, info->array_type->name); + p = __stpcpy(p, " out of bounds"); __ubsan_abort(&info->location, buf); } @@ -328,19 +251,19 @@ void __ubsan_handle_type_mismatch(struct UbsanTypeMismatchInfo *info, if (!pointer) __ubsan_abort(&info->location, "null pointer access"); kind = __ubsan_dubnul(kUbsanTypeCheckKinds, info->type_check_kind); if (info->alignment && (pointer & (info->alignment - 1))) { - p = __ubsan_stpcpy(p, "unaligned "); - p = __ubsan_stpcpy(p, kind), *p++ = ' '; - p = __ubsan_stpcpy(p, info->type->name), *p++ = ' ', *p++ = '@'; + p = __stpcpy(p, "unaligned "); + p = __stpcpy(p, kind), *p++ = ' '; + p = __stpcpy(p, info->type->name), *p++ = ' ', *p++ = '@'; p = __ubsan_itpcpy(p, info->type, pointer); - p = __ubsan_stpcpy(p, " align "); - p = __ubsan_intcpy(p, info->alignment); + p = __stpcpy(p, " align "); + p = __intcpy(p, info->alignment); } else { - p = __ubsan_stpcpy(p, "insufficient size\r\n\t"); - p = __ubsan_stpcpy(p, kind); - p = __ubsan_stpcpy(p, " address 0x"); - p = __ubsan_hexcpy(p, pointer, sizeof(pointer) * CHAR_BIT); - p = __ubsan_stpcpy(p, " with insufficient space for object of type "); - p = __ubsan_stpcpy(p, info->type->name); + p = __stpcpy(p, "insufficient size\r\n\t"); + p = __stpcpy(p, kind); + p = __stpcpy(p, " address 0x"); + p = __fixcpy(p, pointer, sizeof(pointer) * CHAR_BIT); + p = __stpcpy(p, " with insufficient space for object of type "); + p = __stpcpy(p, info->type->name); } __ubsan_abort(&info->location, buf); } diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index 33633725213..d3ac2922ba1 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -31,13 +31,17 @@ #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/nexgen32e/gc.internal.h" +#include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" +#include "libc/stdio/append.internal.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sig.h" +#include "libc/x/x.h" #define kBacktraceMaxFrames 128 #define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (18 + 1)) @@ -168,13 +172,14 @@ static noasan int PrintBacktrace(int fd, const struct StackFrame *bp) { } noasan void ShowBacktrace(int fd, const struct StackFrame *bp) { + /* asan runtime depends on this function */ static bool noreentry; - ++ftrace; + ++g_ftrace; if (!bp) bp = __builtin_frame_address(0); if (!noreentry) { noreentry = true; PrintBacktrace(fd, bp); noreentry = false; } - --ftrace; + --g_ftrace; } diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index dda1b05a20e..74a7318cd8a 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -31,6 +31,8 @@ #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" +#define LIMIT 100 + /** * Prints stack frames with symbols. * @@ -46,14 +48,18 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, struct SymbolTable *st) { size_t gi; intptr_t addr; - int symbol, addend; + int i, symbol, addend; struct Garbages *garbage; const struct StackFrame *frame; - ++ftrace; + ++g_ftrace; if (!bp) bp = __builtin_frame_address(0); garbage = weaken(__garbage); gi = garbage ? garbage->i : 0; - for (frame = bp; frame; frame = frame->next) { + for (i = 0, frame = bp; frame; frame = frame->next) { + if (++i == LIMIT) { + __printf("\n"); + break; + } addr = frame->addr; if (addr == weakaddr("__gc")) { do { @@ -68,15 +74,16 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, * __restore_rt where the kernel creates a stack frame that points * to the beginning of the function. */ - if ((symbol = GetSymbol(st, addr - 1)) != -1 || - (symbol = GetSymbol(st, addr - 0)) != -1) { + if ((symbol = __get_symbol(st, addr - 1)) != -1 || + (symbol = __get_symbol(st, addr - 0)) != -1) { addend = addr - st->addr_base; addend -= st->symbols[symbol].x; } else { addend = 0; } - __printf("%p %p %s%+d\r\n", frame, addr, GetSymbolName(st, symbol), addend); + __printf("%p %p %s%+d\r\n", frame, addr, __get_symbol_name(st, symbol), + addend); } - --ftrace; + --g_ftrace; return 0; } diff --git a/libc/log/cxaprintexits.c b/libc/log/cxaprintexits.c new file mode 100644 index 00000000000..a02d7749002 --- /dev/null +++ b/libc/log/cxaprintexits.c @@ -0,0 +1,60 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/log/log.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/runtime/cxaatexit.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" + +/** + * Prints global destructors. + * + * @param pred can be null to match all + */ +void __cxa_printexits(FILE *f, void *pred) { + char name[23]; + unsigned i, mask; + const char *symbol; + struct CxaAtexitBlock *b; + fprintf(f, "\n"); + fprintf(f, " GLOBAL DESTRUCTORS \n"); + fprintf(f, " callback arg pred \n"); + fprintf(f, "---------------------- ------------------ ------------------\n"); + if ((b = __cxa_blocks.p)) { + do { + mask = b->mask; + while (mask) { + i = bsf(mask); + mask &= ~(1u << i); + if (!pred || pred == b->p[i].pred) { + symbol = __get_symbol_by_addr((intptr_t)b->p[i].fp); + if (symbol) { + snprintf(name, sizeof(name), "%s", symbol); + } else { + snprintf(name, sizeof(name), "0x%016lx", b->p[i].fp); + } + fprintf(f, "%-22s 0x%016lx 0x%016lx\n", name, b->p[i].arg, + b->p[i].pred); + } + } + } while ((b = b->next)); + } + fprintf(f, "\n"); +} diff --git a/libc/log/die.c b/libc/log/die.c index 0f91a713fd0..814ca27251b 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -29,6 +29,7 @@ * If a debugger is present then this will trigger a breakpoint. */ relegated wontreturn void __die(void) { + /* asan runtime depends on this function */ static bool once; if (cmpxchg(&once, false, true)) { __restore_tty(1); diff --git a/libc/log/getcallername.c b/libc/log/getcallername.c index f7bfd8d6f24..cfdb1bf83c4 100644 --- a/libc/log/getcallername.c +++ b/libc/log/getcallername.c @@ -26,6 +26,6 @@ const char *GetCallerName(const struct StackFrame *bp) { struct SymbolTable *st; if (!bp && (bp = __builtin_frame_address(0))) bp = bp->next; - if (bp) return GetSymbolByAddr(bp->addr - 1); + if (bp) return __get_symbol_by_addr(bp->addr); return 0; } diff --git a/libc/log/getsymbolbyaddr.c b/libc/log/getsymbolbyaddr.c new file mode 100644 index 00000000000..857eca338e4 --- /dev/null +++ b/libc/log/getsymbolbyaddr.c @@ -0,0 +1,34 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" + +/** + * Returns name of symbol at address. + */ +noasan char *__get_symbol_by_addr(int64_t addr) { + /* asan runtime depends on this function */ + int i; + struct SymbolTable *st; + st = GetSymbolTable(); + i = __get_symbol(st, addr); + if (i == -1) i = __get_symbol(st, addr - 1); + return __get_symbol_name(st, i); +} diff --git a/libc/log/getsymboltable.c b/libc/log/getsymboltable.c index 77b3a3ed285..c8388f2dfe4 100644 --- a/libc/log/getsymboltable.c +++ b/libc/log/getsymboltable.c @@ -24,18 +24,16 @@ * Returns debug binary symbol table, as global singleton. * @return symbol table, or NULL w/ errno on first call */ -struct SymbolTable *GetSymbolTable(void) { +noasan struct SymbolTable *GetSymbolTable(void) { + /* asan runtime depends on this function */ static bool once; static struct SymbolTable *singleton; const char *debugbin; if (!once) { once = true; - ++ftrace; - if ((debugbin = FindDebugBinary()) && - (singleton = OpenSymbolTable(debugbin))) { - __cxa_atexit(CloseSymbolTable, &singleton, NULL); - } - --ftrace; + ++g_ftrace; + singleton = OpenSymbolTable(FindDebugBinary()); + --g_ftrace; } return singleton; } diff --git a/libc/log/internal.h b/libc/log/internal.h index 52cc58a3024..0d59b9b3390 100644 --- a/libc/log/internal.h +++ b/libc/log/internal.h @@ -7,9 +7,11 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -extern int kCrashSigs[8] hidden; -extern struct termios g_oldtermios hidden; -extern struct sigaction g_oldcrashacts[8] hidden; +extern hidden int kCrashSigs[8]; +extern hidden bool g_isrunningundermake; +extern hidden bool g_isterminalinarticulate; +extern hidden struct termios g_oldtermios; +extern hidden struct sigaction g_oldcrashacts[8]; void __start_fatal(const char *, int) hidden; void __start_fatal_ndebug(void) hidden; diff --git a/libc/log/libfatal.internal.h b/libc/log/libfatal.internal.h index 9d1e6856e4a..aeb5e5c66c0 100644 --- a/libc/log/libfatal.internal.h +++ b/libc/log/libfatal.internal.h @@ -5,42 +5,94 @@ #include "libc/nexgen32e/bsr.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" +#include "libc/runtime/runtime.h" #include "libc/sysv/consts/nr.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ extern char __fatalbuf[]; +void __printf(const char *, ...); + +forceinline void __sysv_exit(long rc) { + asm volatile("call\t__syscall__" + : /* no outputs */ + : "a"(__NR_exit_group), "D"(rc) + : "memory", "cc"); +} + +forceinline int __sysv_close(long fd) { + long ax; + asm volatile("call\t__syscall__" + : "=a"(ax) + : "0"(__NR_close), "D"(fd) + : "rdx", "memory", "cc"); + return ax; +} + +forceinline int __sysv_open(const char *path, long flags, long mode) { + long ax, dx; + asm volatile("call\t__syscall__" + : "=a"(ax), "=d"(dx) + : "0"(__NR_open), "D"(path), "S"(flags), "1"(mode) + : "memory", "cc"); + return ax; +} + +forceinline long __sysv_read(long fd, void *data, unsigned long size) { + long ax, dx; + asm volatile("call\t__syscall__" + : "=a"(ax), "=d"(dx) + : "0"(__NR_read), "D"(fd), "S"(data), "1"(size) + : "memory", "cc"); + return ax; +} + +forceinline long __sysv_write(long fd, const void *data, unsigned long size) { + long ax, dx; + asm volatile("call\t__syscall__" + : "=a"(ax), "=d"(dx) + : "0"(__NR_write), "D"(fd), "S"(data), "1"(size) + : "memory", "cc"); + return ax; +} + +forceinline long __sysv_mprotect(void *addr, size_t size, long prot) { + long ax, dx; + asm volatile("call\t__syscall__" + : "=a"(ax), "=d"(dx) + : "0"(__NR_mprotect), "D"(addr), "S"(size), "1"(prot) + : "memory", "cc"); + return ax; +} + +forceinline int __sysv_getpid(void) { + long ax; + asm volatile("call\t__syscall__" + : "=a"(ax) + : "0"(__NR_getpid) + : "rdx", "memory", "cc"); + return ax; +} + forceinline int __getpid(void) { - int rc; if (!IsWindows()) { - asm volatile("call\t__syscall__" - : "=a"(rc) - : "0"(__NR_getpid) - : "rcx", "r11", "memory"); - return rc; + return __sysv_getpid(); } else { return GetCurrentProcessId(); } } -forceinline ssize_t __write(const void *data, size_t size) { +forceinline ssize_t __write(const void *p, size_t n) { char cf; ssize_t rc; uint32_t wrote; if (!IsWindows()) { - asm volatile("call\t__syscall__" - : "=@ccc"(cf), "=a"(rc) - : "1"(__NR_write), "D"(2), "S"(data), "d"(size) - : "rcx", "r11", "memory"); - if (cf && IsBsd()) rc = -rc; - return rc; + return __sysv_write(2, p, n); + } else if (WriteFile(GetStdHandle(kNtStdErrorHandle), p, n, &wrote, 0)) { + return wrote; } else { - if (WriteFile(GetStdHandle(kNtStdErrorHandle), data, size, &wrote, 0)) { - return wrote; - } else { - return -1; - } + return -GetLastError(); } } @@ -54,6 +106,12 @@ forceinline ssize_t __write_str(const char *s) { return __write(s, __strlen(s)); } +forceinline int __strcmp(const char *l, const char *r) { + size_t i = 0; + while (l[i] == r[i] && r[i]) ++i; + return (l[i] & 255) - (r[i] & 255); +} + forceinline char *__stpcpy(char *d, const char *s) { size_t i; for (i = 0;; ++i) { @@ -63,6 +121,13 @@ forceinline char *__stpcpy(char *d, const char *s) { } } +forceinline void *__repstosb(void *di, char al, size_t cx) { + asm("rep stosb" + : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(cx), "a"(al)); + return di; +} + forceinline void *__repmovsb(void *di, void *si, size_t cx) { asm("rep movsb" : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) @@ -112,7 +177,64 @@ forceinline char *__hexcpy(char p[hasatleast 17], uint64_t x) { return __fixcpy(p, x, ROUNDUP(x ? bsrl(x) + 1 : 1, 4)); } -void __printf(const char *, ...); +forceinline const void *__memchr(const void *s, unsigned char c, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (((const unsigned char *)s)[i] == c) { + return (const unsigned char *)s + i; + } + } + return 0; +} + +forceinline char *__strstr(const char *haystack, const char *needle) { + size_t i; + for (;;) { + for (i = 0;; ++i) { + if (!needle[i]) return (/*unconst*/ char *)haystack; + if (!haystack[i]) break; + if (needle[i] != haystack[i]) break; + } + if (!*haystack++) break; + } + return 0; +} + +forceinline char *__getenv(char **p, const char *s) { + size_t i, j; + if (p) { + for (i = 0; p[i]; ++i) { + for (j = 0;; ++j) { + if (!s[j]) { + if (p[i][j] == '=') { + return p[i] + j + 1; + } + break; + } + if (s[j] != p[i][j]) { + break; + } + } + } + } + return 0; +} + +forceinline unsigned long __atoul(const char *p) { + int c; + unsigned long x = 0; + while ('0' <= (c = *p++) && c <= '9') x *= 10, x += c - '0'; + return x; +} + +forceinline long __atol(const char *p) { + int s = *p; + unsigned long x; + if (s == '-' || s == '+') ++p; + x = __atoul(p); + if (s == '-') x = -x; + return x; +} COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/log/log.h b/libc/log/log.h index 77bd6a6ef8e..dac2adc4784 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -55,8 +55,8 @@ bool32 IsDebuggerPresent(bool); bool IsRunningUnderMake(void); const char *GetSiCodeName(int, int); void AppendResourceReport(char **, struct rusage *, const char *); -char *GetSymbolByAddr(int64_t); -void PrintGarbage(FILE *); +char *__get_symbol_by_addr(int64_t); +void PrintGarbage(void); void PrintGarbageNumeric(FILE *); #define showcrashreports() ShowCrashReports() @@ -75,15 +75,15 @@ extern unsigned __log_level; /* log level for runtime check */ // log a message with the specified log level (not checking if LOGGABLE) #define LOGF(LEVEL, FMT, ...) \ do { \ - ++ftrace; \ + ++g_ftrace; \ flogf(LEVEL, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } while (0) // die with an error message without backtrace and debugger invocation #define DIEF(FMT, ...) \ do { \ - ++ftrace; \ + ++g_ftrace; \ flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ exit(1); \ unreachable; \ @@ -91,7 +91,7 @@ extern unsigned __log_level; /* log level for runtime check */ #define FATALF(FMT, ...) \ do { \ - ++ftrace; \ + ++g_ftrace; \ ffatalf(kLogFatal, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ unreachable; \ } while (0) @@ -99,78 +99,78 @@ extern unsigned __log_level; /* log level for runtime check */ #define ERRORF(FMT, ...) \ do { \ if (LOGGABLE(kLogError)) { \ - ++ftrace; \ + ++g_ftrace; \ flogf(kLogError, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define WARNF(FMT, ...) \ do { \ if (LOGGABLE(kLogWarn)) { \ - ++ftrace; \ + ++g_ftrace; \ flogf(kLogWarn, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define INFOF(FMT, ...) \ do { \ if (LOGGABLE(kLogInfo)) { \ - ++ftrace; \ + ++g_ftrace; \ flogf(kLogInfo, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define VERBOSEF(FMT, ...) \ do { \ if (LOGGABLE(kLogVerbose)) { \ - ++ftrace; \ + ++g_ftrace; \ fverbosef(kLogVerbose, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define DEBUGF(FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogDebug))) { \ - ++ftrace; \ + ++g_ftrace; \ fdebugf(kLogDebug, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define NOISEF(FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogNoise))) { \ - ++ftrace; \ + ++g_ftrace; \ fnoisef(kLogNoise, __FILE__, __LINE__, NULL, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define FLOGF(F, FMT, ...) \ do { \ if (LOGGABLE(kLogInfo)) { \ - ++ftrace; \ + ++g_ftrace; \ flogf(kLogInfo, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define FWARNF(F, FMT, ...) \ do { \ if (LOGGABLE(kLogWarn)) { \ - ++ftrace; \ + ++g_ftrace; \ flogf(kLogWarn, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define FFATALF(F, FMT, ...) \ do { \ - ++ftrace; \ + ++g_ftrace; \ ffatalf(kLogFatal, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ unreachable; \ } while (0) @@ -178,18 +178,18 @@ extern unsigned __log_level; /* log level for runtime check */ #define FDEBUGF(F, FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogDebug))) { \ - ++ftrace; \ + ++g_ftrace; \ fdebugf(kLogDebug, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) #define FNOISEF(F, FMT, ...) \ do { \ if (UNLIKELY(LOGGABLE(kLogNoise))) { \ - ++ftrace; \ + ++g_ftrace; \ fnoisef(kLogNoise, __FILE__, __LINE__, F, FMT, ##__VA_ARGS__); \ - --ftrace; \ + --g_ftrace; \ } \ } while (0) @@ -201,9 +201,9 @@ extern unsigned __log_level; /* log level for runtime check */ ({ \ autotype(FORM) Ax = (FORM); \ if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \ - ++ftrace; \ + ++g_ftrace; \ __logerrno(__FILE__, __LINE__, #FORM); \ - --ftrace; \ + --g_ftrace; \ } \ Ax; \ }) @@ -212,9 +212,9 @@ extern unsigned __log_level; /* log level for runtime check */ ({ \ autotype(FORM) Ax = (FORM); \ if (Ax == NULL && LOGGABLE(kLogWarn)) { \ - ++ftrace; \ + ++g_ftrace; \ __logerrno(__FILE__, __LINE__, #FORM); \ - --ftrace; \ + --g_ftrace; \ } \ Ax; \ }) diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 30c60bc649d..6e2508f35b6 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" @@ -44,6 +45,7 @@ #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/pc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/fileno.h" @@ -94,20 +96,23 @@ relegated static const char *TinyStrSignal(int sig) { return "???"; } -relegated static void ShowFunctionCalls(int fd, ucontext_t *ctx) { +relegated static void ShowFunctionCalls(ucontext_t *ctx) { struct StackFrame *bp; struct StackFrame goodframe; - write(fd, "\n", 1); - if (ctx && ctx->uc_mcontext.rip && ctx->uc_mcontext.rbp) { + if (ctx->uc_mcontext.rip && ctx->uc_mcontext.rbp) { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; bp = &goodframe; - ShowBacktrace(fd, bp); + ShowBacktrace(2, bp); } } relegated static char *AddFlag(char *p, int b, const char *s) { - if (b) p = stpcpy(p, s); + if (b) { + p = __stpcpy(p, s); + } else { + *p = 0; + } return p; } @@ -137,11 +142,13 @@ relegated static char *DescribeCpuFlags(char *p, int flags, int x87sw, return p; } -relegated static char *ShowGeneralRegisters(char *p, ucontext_t *ctx) { +relegated static void ShowGeneralRegisters(ucontext_t *ctx) { int64_t x; const char *s; size_t i, j, k; long double st; + char *p, buf[128]; + p = buf; *p++ = '\n'; for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { if (j > 0) *p++ = ' '; @@ -162,20 +169,25 @@ relegated static char *ShowGeneralRegisters(char *p, ucontext_t *ctx) { if (x < 0) x = -x, *p++ = '-'; p = __uintcpy(p, x / 1000), *p++ = '.'; p = __uintcpy(p, x % 1000), *p++ = '\n'; + *p = 0; + __printf("%s", buf); + p = buf; } } - return DescribeCpuFlags( + DescribeCpuFlags( p, ctx->uc_mcontext.gregs[REG_EFL], ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->swd : 0, ctx->uc_mcontext.fpregs ? ctx->uc_mcontext.fpregs->mxcsr : 0); + __printf("%s\n", buf); } -relegated static char *ShowSseRegisters(char *p, ucontext_t *ctx) { +relegated static void ShowSseRegisters(ucontext_t *ctx) { size_t i; + char *p, buf[128]; if (ctx->uc_mcontext.fpregs) { - p = __stpcpy(p, "\n\n"); + __printf("\n"); for (i = 0; i < 8; ++i) { - p = __stpcpy(p, "XMM"); + p = buf; if (i >= 10) { *p++ = i / 10 + '0'; *p++ = i % 10 + '0'; @@ -197,93 +209,61 @@ relegated static char *ShowSseRegisters(char *p, ucontext_t *ctx) { *p++ = ' '; p = __fixcpy(p, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[1], 64); p = __fixcpy(p, ctx->uc_mcontext.fpregs->xmm[i + 8].u64[0], 64); - *p++ = '\n'; - } - } - return p; -} - -relegated static void ShowMemoryMappings(int outfd) { - ssize_t rc; - int c, infd; - char buf[64]; - if (!IsTiny()) { - PrintMemoryIntervals(outfd, &_mmi); - if ((infd = open("/proc/self/maps", O_RDONLY)) != -1) { - while ((rc = read(infd, buf, sizeof(buf))) > 0) { - __write(buf, rc); - } + *p = 0; + __printf("XMM%s\n", buf); } - close(infd); } } void ShowCrashReportHook(int, int, int, struct siginfo *, ucontext_t *); -relegated void ShowCrashReport(int err, int fd, int sig, struct siginfo *si, +relegated void ShowCrashReport(int err, int sig, struct siginfo *si, ucontext_t *ctx) { int i; char *p; - bool colorful; - char hostname[64]; + char host[64]; + intptr_t stackaddr; struct utsname names; static char buf[4096]; if (weaken(ShowCrashReportHook)) { - ShowCrashReportHook(err, fd, sig, si, ctx); + ShowCrashReportHook(2, err, sig, si, ctx); } - colorful = cancolor(); - __stpcpy(hostname, "unknown"); - gethostname(hostname, sizeof(hostname)); + __stpcpy(host, "unknown"); + gethostname(host, sizeof(host)); p = buf; - p = __stpcpy(p, "\n"); - if (colorful) p = __stpcpy(p, "\e[30;101m"); - p = __stpcpy(p, "error"); - if (colorful) p = __stpcpy(p, "\e[0m"); - p = __stpcpy(p, ": Uncaught SIG"); - p = __stpcpy(p, TinyStrSignal(sig)); - if (si) { - p = __stpcpy(p, " ("); - p = __stpcpy(p, GetSiCodeName(sig, si->si_code)); - p = __stpcpy(p, ")"); + __printf("\n%serror%s: Uncaught SIG%s", + !g_isterminalinarticulate ? "\e[30;101m" : "", + !g_isterminalinarticulate ? "\e[0m" : "", TinyStrSignal(sig)); + stackaddr = GetStackAddr(0); + if (ctx && (ctx->uc_mcontext.rsp >= GetStaticStackAddr(0) && + ctx->uc_mcontext.rsp <= GetStaticStackAddr(0) + PAGESIZE)) { + __printf(" (Stack Overflow)"); + } else if (si) { + __printf(" (%s)", GetSiCodeName(sig, si->si_code)); } - p = __stpcpy(p, " on "); - p = __stpcpy(p, hostname); - p = __stpcpy(p, " pid "); - p = __intcpy(p, __getpid()); - p = __stpcpy(p, "\n "); - p = __stpcpy(p, program_invocation_name); - p = __stpcpy(p, "\n "); - p = __stpcpy(p, strerror(err)); - *p++ = '\n'; + __printf(" on %s pid %d\n %s\n %s\n", host, __getpid(), + program_invocation_name, strerror(err)); if (uname(&names) != -1) { - p = __stpcpy(p, " "); - p = __stpcpy(p, names.sysname), *p++ = ' '; - p = __stpcpy(p, names.nodename), *p++ = ' '; - p = __stpcpy(p, names.release), *p++ = ' '; - p = __stpcpy(p, names.version), *p++ = '\n'; + __printf(" %s %s %s %s\n", names.sysname, names.nodename, names.release, + names.version); } - __write(buf, p - buf); - ShowFunctionCalls(fd, ctx); if (ctx) { - p = buf; - p = ShowGeneralRegisters(p, ctx); - p = ShowSseRegisters(p, ctx); - *p++ = '\n'; - __write(buf, p - buf); + __printf("\n"); + ShowFunctionCalls(ctx); + ShowGeneralRegisters(ctx); + ShowSseRegisters(ctx); } - p = buf; - *p++ = '\n'; - ShowMemoryMappings(fd); - __write(buf, p - buf); + __printf("\n"); + PrintMemoryIntervals(2, &_mmi); + /* PrintSystemMappings(2); */ if (__argv) { for (i = 0; i < __argc; ++i) { if (!__argv[i]) continue; if (IsAsan() && !__asan_is_valid(__argv[i], 1)) continue; - __write(__argv[i], strlen(__argv[i])); - __write(" ", 1); + __printf("%s ", __argv[i]); } } - __write("\n", 1); + __printf("\n"); } relegated static void RestoreDefaultCrashSignalHandlers(void) { @@ -309,29 +289,48 @@ relegated static void RestoreDefaultCrashSignalHandlers(void) { * * This function never returns, except for traps w/ human supervision. */ -relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) { +noasan relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) { intptr_t rip; int gdbpid, err; - static bool once; - err = errno; - if (once) _exit(119); - once = true; + static bool noreentry, notpossible; + ++g_ftrace; rip = ctx ? ctx->uc_mcontext.rip : 0; - if ((gdbpid = IsDebuggerPresent(true))) { - DebugBreak(); - } else if (IsTerminalInarticulate() || IsRunningUnderMake()) { - gdbpid = -1; - } else if (FindDebugBinary()) { - RestoreDefaultCrashSignalHandlers(); - gdbpid = - attachdebugger(((sig == SIGTRAP || sig == SIGQUIT) && - (rip >= (intptr_t)&_base && rip < (intptr_t)&_etext)) - ? rip - : 0); + if (cmpxchg(&noreentry, false, true)) { + err = errno; + if ((gdbpid = IsDebuggerPresent(true))) { + DebugBreak(); + } else if (g_isterminalinarticulate || g_isrunningundermake) { + gdbpid = -1; + } else if (FindDebugBinary()) { + RestoreDefaultCrashSignalHandlers(); + gdbpid = + attachdebugger(((sig == SIGTRAP || sig == SIGQUIT) && + (rip >= (intptr_t)&_base && rip < (intptr_t)&_etext)) + ? rip + : 0); + } + if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) { + __restore_tty(1); + ShowCrashReport(err, sig, si, ctx); + _Exit(128 + sig); + } + } else if (cmpxchg(¬possible, false, true)) { + __printf("\n" + "\n" + "CRASHED WHILE CRASHING WITH SIG%s\n" + "%s\n" + "RIP %x\n" + "RSP %x\n" + "RBP %x\n" + "\n", + TinyStrSignal(sig), __argv[0], rip, ctx ? ctx->uc_mcontext.rsp : 0, + ctx ? ctx->uc_mcontext.rbp : 0); + _Exit(119); + } else { + for (;;) { + asm("ud2"); + } } - if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return; - __restore_tty(1); - ShowCrashReport(err, STDERR_FILENO, sig, si, ctx); - exit(128 + sig); - unreachable; + noreentry = false; + --g_ftrace; } diff --git a/libc/log/printgarbage.c b/libc/log/printgarbage.c index 30bfecb8ced..7c8d8bd4961 100644 --- a/libc/log/printgarbage.c +++ b/libc/log/printgarbage.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/nexgen32e/gc.internal.h" #include "libc/stdio/stdio.h" @@ -24,19 +26,31 @@ /** * Prints list of deferred operations on shadow stack. */ -void PrintGarbage(FILE *f) { +void PrintGarbage(void) { size_t i; - f = stderr; - fprintf(f, "\n"); - fprintf(f, " SHADOW STACK @ 0x%016lx\n", __builtin_frame_address(0)); - fprintf(f, " garbage entry parent frame original ret callback arg \n"); - fprintf(f, "-------------- -------------- ------------------ ------------------ ------------------\n"); - for (i = __garbage.i; i--;) { - fprintf(f, "0x%012lx 0x%012lx %-18s %-18s 0x%016lx\n", - __garbage.p + i, - __garbage.p[i].frame, - GetSymbolByAddr(__garbage.p[i].ret-1), - GetSymbolByAddr(__garbage.p[i].fn), - __garbage.p[i].arg); + char name[19]; + const char *symbol; + __printf("\n"); + __printf(" SHADOW STACK @ 0x%p\n", __builtin_frame_address(0)); + __printf("garbage entry parent frame original ret callback arg \n"); + __printf("-------------- -------------- ------------------ ------------------ ------------------\n"); + if (__garbage.i) { + for (i = __garbage.i; i--;) { + symbol = __get_symbol_by_addr(__garbage.p[i].ret); + if (symbol) { + snprintf(name, sizeof(name), "%s", symbol); + } else { + snprintf(name, sizeof(name), "0x%012lx", __garbage.p[i].ret); + } + __printf("0x%p 0x%p %18s %18s 0x%016lx\n", + __garbage.p + i, + __garbage.p[i].frame, + name, + __get_symbol_by_addr(__garbage.p[i].fn), + __garbage.p[i].arg); + } + } else { + __printf("%14s %14s %18s %18s %18s\n","empty","-","-","-","-"); } + __printf("\n"); } diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 2a0bba4a47f..cfca9561008 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -16,22 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/bits.h" -#include "libc/calls/calls.h" #include "libc/calls/sigbits.h" -#include "libc/calls/termios.h" -#include "libc/calls/typedef/sigaction_f.h" -#include "libc/dce.h" -#include "libc/log/check.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigaltstack.h" #include "libc/log/internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" -#include "libc/nt/signals.h" -#include "libc/runtime/symbols.internal.h" -#include "libc/str/str.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/ss.h" STATIC_YOINK("__die"); @@ -57,6 +50,7 @@ extern const unsigned char __oncrash_thunks[8][11]; void ShowCrashReports(void) { size_t i; struct sigaction sa; + struct sigaltstack ss; /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ kCrashSigs[0] = SIGQUIT; /* ctrl+\ aka ctrl+break */ kCrashSigs[1] = SIGFPE; /* 1 / 0 */ @@ -68,11 +62,16 @@ void ShowCrashReports(void) { kCrashSigs[7] = SIGPIPE; /* write to closed thing */ /* : showcrashreports.c, oncrashthunks.S, oncrash.c */ bzero(&sa, sizeof(sa)); - sa.sa_flags = SA_RESETHAND | SA_SIGINFO; + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + ss.ss_sp = malloc(SIGSTKSZ); + __cxa_atexit(free, ss.ss_sp, 0); + sa.sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK; sigfillset(&sa.sa_mask); for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { sigdelset(&sa.sa_mask, kCrashSigs[i]); } + sigaltstack(&ss, 0); for (i = 0; i < ARRAYLEN(kCrashSigs); ++i) { if (kCrashSigs[i]) { sa.sa_sigaction = (sigaction_f)__oncrash_thunks[i]; diff --git a/libc/mem/hook/posix_memalign.S b/libc/mem/aligned_alloc.c similarity index 73% rename from libc/mem/hook/posix_memalign.S rename to libc/mem/aligned_alloc.c index 750c606b98c..fc2133098df 100644 --- a/libc/mem/hook/posix_memalign.S +++ b/libc/mem/aligned_alloc.c @@ -1,7 +1,7 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,18 +16,23 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" #include "libc/macros.internal.h" -.source __FILE__ +#include "libc/mem/mem.h" - .initbss 202,_init_posix_memalign -hook_posix_memalign: - .quad 0 - .endobj hook_posix_memalign,globl,hidden - .previous - - .init.start 202,_init_posix_memalign - .hidden dlposix_memalign - ezlea dlposix_memalign,ax - stosq - yoink free - .init.end 202,_init_posix_memalign +/** + * Same as memalign(a, n) but requires IS2POW(a). + * + * @param n number of bytes needed + * @return memory address, or NULL w/ errno + * @throw EINVAL if !IS2POW(a) + * @see pvalloc() + */ +void *aligned_alloc(size_t a, size_t n) { + if (IS2POW(a)) { + return memalign(a, n); + } else { + errno = EINVAL; + return 0; + } +} diff --git a/libc/mem/arena.c b/libc/mem/arena.c index 0564ad82f1e..233dd05c0dc 100644 --- a/libc/mem/arena.c +++ b/libc/mem/arena.c @@ -22,37 +22,39 @@ #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/limits.h" -#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/mem/arena.h" #include "libc/mem/hook/hook.internal.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/nexgen32e/bsr.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/errfuns.h" -#define BASE ((char *)0x30000000) -#define LIMIT ((char *)0x50000000) - +#define BASE 0x50000000 +#define SIZE 0x2ffe0000 +#define P(i) ((void *)(intptr_t)(i)) #define EXCHANGE(HOOK, SLOT) \ __arena_hook((intptr_t *)weaken(HOOK), (intptr_t *)(&(SLOT))) static struct Arena { bool once; - uint8_t depth; - unsigned size; - unsigned offset[16]; + size_t size; + size_t depth; + size_t offset[16]; void (*free)(void *); void *(*malloc)(size_t); void *(*calloc)(size_t, size_t); void *(*memalign)(size_t, size_t); void *(*realloc)(void *, size_t); void *(*realloc_in_place)(void *, size_t); - void *(*valloc)(size_t); - void *(*pvalloc)(size_t); - int (*malloc_trim)(size_t); size_t (*malloc_usable_size)(const void *); size_t (*bulk_free)(void *[], size_t); + int (*malloc_trim)(size_t); } __arena; static wontreturn void __arena_die(void) { @@ -61,101 +63,200 @@ static wontreturn void __arena_die(void) { } static wontreturn void __arena_not_implemented(void) { - __printf("not implemented"); + assert(!"not implemented"); __arena_die(); } -static void __arena_free(void *p) { - if (!p) return; +forceinline void __arena_check(void) { assert(__arena.depth); - assert((intptr_t)BASE + __arena.offset[__arena.depth - 1] <= (intptr_t)p && - (intptr_t)p < (intptr_t)BASE + __arena.offset[__arena.depth]); +} + +forceinline void __arena_check_pointer(void *p) { + assert(BASE + __arena.offset[__arena.depth - 1] <= (uintptr_t)p && + (uintptr_t)p < BASE + __arena.offset[__arena.depth]); +} + +forceinline bool __arena_is_arena_pointer(void *p) { + return BASE <= (uintptr_t)p && (uintptr_t)p < BASE + SIZE; +} + +forceinline size_t __arena_get_size(void *p) { + return *(const size_t *)((const char *)p - sizeof(size_t)); +} + +static void __arena_free(void *p) { + __arena_check(); + if (p) { + __arena_check_pointer(p); + if (!(BASE <= (uintptr_t)p && (uintptr_t)p < BASE + SIZE)) { + __arena.free(p); + } + } } static size_t __arena_bulk_free(void *p[], size_t n) { size_t i; for (i = 0; i < n; ++i) { - if (p[i]) __arena_free(p[i]); + __arena_free(p[i]); + p[i] = 0; } - bzero(p, n * sizeof(void *)); return 0; } -static void *__arena_malloc(size_t n) { - char *ptr; - size_t need, greed; - assert(__arena.depth); +static noinline bool __arena_grow(size_t offset, size_t request) { + size_t greed; + greed = __arena.size + 1; + do { + greed += greed >> 1; + greed = ROUNDUP(greed, FRAMESIZE); + } while (greed < offset + request); + if (greed <= SIZE) { + if (mmap(P(BASE + __arena.size), greed - __arena.size, + PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, + -1, 0) != MAP_FAILED) { + __arena.size = greed; + return true; + } + } else { + enomem(); + } + if (weaken(__oom_hook)) { + weaken(__oom_hook)(request); + } + return false; +} + +static void *__arena_alloc(size_t a, size_t n) { + size_t o; if (!n) n = 1; - if (n < LIMIT - BASE) { - need = __arena.offset[__arena.depth] + n; - need = ROUNDUP(need, __BIGGEST_ALIGNMENT__); - if (UNLIKELY(need > __arena.size)) { - greed = __arena.size + 1; - do { - greed += greed >> 1; - greed = ROUNDUP(greed, FRAMESIZE); - } while (need > greed); - if (greed < LIMIT - BASE && - mmap(BASE + __arena.size, greed - __arena.size, - PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, - -1, 0) != MAP_FAILED) { - __arena.size = greed; - } else { - return 0; + o = ROUNDUP(__arena.offset[__arena.depth] + sizeof(size_t), a); + if (o + n >= n) { + if (n <= sizeof(size_t)) { + n = sizeof(size_t); + } else { + n = ROUNDUP(n, sizeof(size_t)); + } + if (o + n <= SIZE) { + if (UNLIKELY(o + n > __arena.size)) { + if (!__arena_grow(o, n)) return 0; } + __arena.offset[__arena.depth] = o + n; + *(size_t *)(BASE + o - sizeof(size_t)) = n; + return (void *)(BASE + o); } - ptr = BASE + __arena.offset[__arena.depth]; - __arena.offset[__arena.depth] = need; - return ptr; - } else { - return 0; } + enomem(); + return 0; +} + +static void *__arena_malloc(size_t n) { + __arena_check(); + return __arena_alloc(16, n); } static void *__arena_calloc(size_t n, size_t z) { + __arena_check(); if (__builtin_mul_overflow(n, z, &n)) n = -1; - return __arena_malloc(n); + return __arena_alloc(16, n); } static void *__arena_memalign(size_t a, size_t n) { - if (a <= __BIGGEST_ALIGNMENT__) { - return __arena_malloc(n); + __arena_check(); + if (a <= sizeof(size_t)) { + return __arena_alloc(8, n); + } else { + return __arena_alloc(2ul << bsrl(a - 1), n); + } +} + +static size_t __arena_malloc_usable_size(const void *p) { + __arena_check(); + __arena_check_pointer(p); + if (__arena_is_arena_pointer(p)) { + return __arena_get_size(p); } else { - __arena_not_implemented(); + return __arena.malloc_usable_size(p); } } static void *__arena_realloc(void *p, size_t n) { + char *q; + size_t m, o, z; + __arena_check(); if (p) { - if (n) { - __arena_not_implemented(); + __arena_check_pointer(p); + if (__arena_is_arena_pointer(p)) { + if (n) { + if ((m = __arena_get_size(p)) >= n) { + return p; + } else if (n <= SIZE) { + z = 2ul << bsrl(n - 1); + if (__arena.offset[__arena.depth] - m == (o = (intptr_t)p - BASE)) { + if (UNLIKELY(o + z > __arena.size)) { + if (o + z <= SIZE) { + if (!__arena_grow(o, z)) { + return 0; + } + } else { + enomem(); + return 0; + } + } + __arena.offset[__arena.depth] = o + z; + *(size_t *)((char *)p - sizeof(size_t)) = z; + return p; + } else if ((q = __arena_alloc(1ul << bsfl((intptr_t)p), z))) { + memmove(q, p, m); + return q; + } else { + return 0; + } + } else { + enomem(); + return 0; + } + } else { + return 0; + } } else { - __arena_free(p); - return 0; + return __arena.realloc(p, n); } } else { - return __arena_malloc(n); + if (n <= 16) { + n = 16; + } else { + n = 2ul << bsrl(n - 1); + } + return __arena_alloc(16, n); } } -static int __arena_malloc_trim(size_t n) { - return 0; -} - static void *__arena_realloc_in_place(void *p, size_t n) { - __arena_not_implemented(); -} - -static void *__arena_valloc(size_t n) { - __arena_not_implemented(); -} - -static void *__arena_pvalloc(size_t n) { - __arena_not_implemented(); + char *q; + size_t m, z; + __arena_check(); + if (p) { + __arena_check_pointer(p); + if (__arena_is_arena_pointer(p)) { + if (n) { + if ((m = __arena_get_size(p)) >= n) { + return p; + } else { + return 0; + } + } else { + return 0; + } + } else { + return __arena.realloc_in_place(p, n); + } + } else { + return 0; + } } -static size_t __arena_malloc_usable_size(const void *p) { - __arena_not_implemented(); +static int __arena_malloc_trim(size_t n) { + return 0; } static void __arena_hook(intptr_t *h, intptr_t *f) { @@ -169,42 +270,32 @@ static void __arena_hook(intptr_t *h, intptr_t *f) { static void __arena_install(void) { EXCHANGE(hook_free, __arena.free); - EXCHANGE(hook_realloc, __arena.realloc); - EXCHANGE(hook_realloc, __arena.realloc); EXCHANGE(hook_malloc, __arena.malloc); EXCHANGE(hook_calloc, __arena.calloc); + EXCHANGE(hook_realloc, __arena.realloc); EXCHANGE(hook_memalign, __arena.memalign); - EXCHANGE(hook_realloc_in_place, __arena.realloc_in_place); - EXCHANGE(hook_valloc, __arena.valloc); - EXCHANGE(hook_pvalloc, __arena.pvalloc); + EXCHANGE(hook_bulk_free, __arena.bulk_free); EXCHANGE(hook_malloc_trim, __arena.malloc_trim); + EXCHANGE(hook_realloc_in_place, __arena.realloc_in_place); EXCHANGE(hook_malloc_usable_size, __arena.malloc_usable_size); - EXCHANGE(hook_bulk_free, __arena.bulk_free); } static void __arena_destroy(void) { - if (__arena.depth) { - __arena_install(); - } - if (__arena.size) { - munmap(BASE, __arena.size); - } + if (__arena.depth) __arena_install(); + if (__arena.size) munmap(P(BASE), __arena.size); bzero(&__arena, sizeof(__arena)); } static void __arena_init(void) { __arena.free = __arena_free; - __arena.realloc = __arena_realloc; - __arena.realloc = __arena_realloc; __arena.malloc = __arena_malloc; __arena.calloc = __arena_calloc; + __arena.realloc = __arena_realloc; __arena.memalign = __arena_memalign; - __arena.realloc_in_place = __arena_realloc_in_place; - __arena.valloc = __arena_valloc; - __arena.pvalloc = __arena_pvalloc; + __arena.bulk_free = __arena_bulk_free; __arena.malloc_trim = __arena_malloc_trim; + __arena.realloc_in_place = __arena_realloc_in_place; __arena.malloc_usable_size = __arena_malloc_usable_size; - __arena.bulk_free = __arena_bulk_free; atexit(__arena_destroy); } @@ -215,24 +306,27 @@ void __arena_push(void) { } if (!__arena.depth) { __arena_install(); - } else if (__arena.depth == ARRAYLEN(__arena.offset) - 1) { - __printf("too many arenas"); - __arena_die(); + } else { + assert(__arena.depth < ARRAYLEN(__arena.offset) - 1); } __arena.offset[__arena.depth + 1] = __arena.offset[__arena.depth]; ++__arena.depth; } void __arena_pop(void) { - unsigned greed; - assert(__arena.depth); - bzero(BASE + __arena.offset[__arena.depth - 1], - __arena.offset[__arena.depth] - __arena.offset[__arena.depth - 1]); + size_t a, b, greed; + __arena_check(); if (!--__arena.depth) __arena_install(); - greed = __arena.offset[__arena.depth]; + a = __arena.offset[__arena.depth]; + b = __arena.offset[__arena.depth + 1]; + greed = a; greed += FRAMESIZE; greed <<= 1; if (__arena.size > greed) { - munmap(BASE + greed, __arena.size - greed); + munmap(P(BASE + greed), __arena.size - greed); + __arena.size = greed; + b = MIN(b, greed); + a = MIN(b, a); } + bzero(P(BASE + a), b - a); } diff --git a/libc/mem/balloc.c b/libc/mem/balloc.c index a3b7f8cce91..1f2894e7212 100644 --- a/libc/mem/balloc.c +++ b/libc/mem/balloc.c @@ -19,6 +19,8 @@ #include "libc/mem/mem.h" #include "libc/runtime/buffer.h" +/* TODO(jart): delete */ + #define kGuard PAGESIZE #define kGrain FRAMESIZE diff --git a/libc/mem/defer.greg.c b/libc/mem/defer.greg.c index 768763d5663..d22aacd3ab6 100644 --- a/libc/mem/defer.greg.c +++ b/libc/mem/defer.greg.c @@ -18,11 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/likely.h" +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/gc.internal.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/str/str.h" forceinline bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame, struct StackFrame *parent, @@ -31,14 +33,28 @@ forceinline bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame, ((intptr_t)ptr < (intptr_t)parent)); } +static void __garbage_destroy(void) { + if (weaken(free)) { + weaken(free)(__garbage.p); + } + bzero(&__garbage, sizeof(__garbage)); +} + void __deferer(struct StackFrame *frame, void *fn, void *arg) { size_t n2; struct Garbage *p2; if (UNLIKELY(__garbage.i == __garbage.n)) { + p2 = __garbage.p; n2 = __garbage.n + (__garbage.n >> 1); - p2 = malloc(n2 * sizeof(*__garbage.p)); - memcpy(p2, __garbage.p, __garbage.n * sizeof(*__garbage.p)); - if (__garbage.p != __garbage.initmem) free(__garbage.p); + if (__garbage.p != __garbage.initmem) { + if (!weaken(realloc)) return; + if (!(p2 = weaken(realloc)(p2, n2 * sizeof(*p2)))) return; + } else { + if (!weaken(malloc)) return; + if (!(p2 = weaken(malloc)(n2 * sizeof(*p2)))) return; + memcpy(p2, __garbage.p, __garbage.n * sizeof(*p2)); + atexit(__garbage_destroy); + } __garbage.p = p2; __garbage.n = n2; } @@ -59,11 +75,11 @@ void __deferer(struct StackFrame *frame, void *fn, void *arg) { * @return arg */ void __defer(struct StackFrame *frame, void *fn, void *arg) { - struct StackFrame *f2; + struct StackFrame *f; if (!arg) return; - f2 = __builtin_frame_address(0); + f = __builtin_frame_address(0); assert(__garbage.n); - assert(f2->next == frame); - assert(PointerNotOwnedByParentStackFrame(f2, frame, arg)); + assert(f->next == frame); + assert(PointerNotOwnedByParentStackFrame(f, frame, arg)); __deferer(frame, fn, arg); } diff --git a/libc/mem/hook/hook.internal.h b/libc/mem/hook/hook.internal.h index b890435d475..6e92ee96018 100644 --- a/libc/mem/hook/hook.internal.h +++ b/libc/mem/hook/hook.internal.h @@ -9,8 +9,6 @@ extern void *(*hook_calloc)(size_t, size_t); extern void *(*hook_memalign)(size_t, size_t); extern void *(*hook_realloc)(void *, size_t); extern void *(*hook_realloc_in_place)(void *, size_t); -extern void *(*hook_valloc)(size_t); -extern void *(*hook_pvalloc)(size_t); extern int (*hook_malloc_trim)(size_t); extern size_t (*hook_malloc_usable_size)(const void *); extern size_t (*hook_bulk_free)(void *[], size_t); diff --git a/libc/mem/internal.h b/libc/mem/internal.h index fa3f1f23336..c6fb3ad3bef 100644 --- a/libc/mem/internal.h +++ b/libc/mem/internal.h @@ -4,6 +4,7 @@ COSMOPOLITAN_C_START_ int PutEnvImpl(char *, bool) hidden; +void __freeenv(void *) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/mem/malloc.S b/libc/mem/malloc.S index e678191c7a8..3e7388c6651 100644 --- a/libc/mem/malloc.S +++ b/libc/mem/malloc.S @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -.source __FILE__ // Allocates uninitialized memory. // @@ -32,9 +31,7 @@ // fail. The maximum supported value of n differs across systems, but is // in all cases less than the maximum representable value of a size_t. // -// @param rdi is number of bytes needed +// @param rdi is number of bytes needed, coerced to 1+ // @return new memory, or NULL w/ errno -// @note malloc(0) → malloc(32) -// @see dlmalloc() malloc: jmp *hook_malloc(%rip) .endfn malloc,globl diff --git a/libc/mem/mem.h b/libc/mem/mem.h index d2e711cbd44..4d772fd7e10 100644 --- a/libc/mem/mem.h +++ b/libc/mem/mem.h @@ -15,15 +15,17 @@ void free(void *) libcesque; void *malloc(size_t) attributeallocsize((1)) mallocesque; void *calloc(size_t, size_t) attributeallocsize((1, 2)) mallocesque; void *memalign(size_t, size_t) attributeallocalign((1)) - attributeallocsize((2)) mallocesque; + attributeallocsize((2)) returnspointerwithnoaliases libcesque nodiscard; void *realloc(void *, size_t) reallocesque; -void *realloc_in_place(void *, size_t); +void *realloc_in_place(void *, size_t) reallocesque; void *reallocarray(void *, size_t, size_t) nodiscard; void *valloc(size_t) attributeallocsize((1)) vallocesque; -void *pvalloc(size_t) attributeallocsize((1)) mallocesque; +void *pvalloc(size_t) vallocesque; char *strdup(const char *) paramsnonnull() mallocesque; char *strndup(const char *, size_t) paramsnonnull() mallocesque; -int posix_memalign(void **, size_t, size_t); /* wut */ +void *aligned_alloc(size_t, size_t) attributeallocsize((1)) + attributeallocsize((2)) returnspointerwithnoaliases libcesque nodiscard; +int posix_memalign(void **, size_t, size_t); bool __grow(void *, size_t *, size_t, size_t) paramsnonnull((1, 2)) libcesque; int malloc_trim(size_t); diff --git a/libc/mem/memalign.S b/libc/mem/memalign.S index 247046e728c..9c75364f669 100644 --- a/libc/mem/memalign.S +++ b/libc/mem/memalign.S @@ -18,22 +18,18 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" #include "libc/notice.inc" -.source __FILE__ // Allocates aligned memory. // // Returns a pointer to a newly allocated chunk of n bytes, aligned in -// accord with the alignment argument. The alignment argument should be -// a power of two. If the argument is not a power of two, the nearest -// greater power is used. 8-byte alignment is guaranteed by normal -// malloc calls, so don't bother calling memalign with an argument of 8 -// or less. +// accord with the alignment argument. The alignment argument shall be +// rounded up to the nearest two power and higher 2 powers may be used +// if the allocator imposes a minimum alignment requirement. // -// @param rdi is alignment in bytes -// @param rsi (newsize) is number of bytes needed +// @param rdi is alignment in bytes, coerced to 1+ w/ 2-power roundup +// @param rsi is number of bytes needed, coerced to 1+ // @return rax is memory address, or NULL w/ errno -// @note overreliance on memalign is a sure way to fragment space -// @see dlmemalign() +// @see valloc(), pvalloc() memalign: jmp *hook_memalign(%rip) .endfn memalign,globl diff --git a/libc/mem/posix_memalign.S b/libc/mem/posix_memalign.S deleted file mode 100644 index f2b551d3638..00000000000 --- a/libc/mem/posix_memalign.S +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.internal.h" -.source __FILE__ - -// Allocates aligned memory the POSIX way. -// -// Allocates a chunk of n bytes, aligned in accord with the alignment -// argument. Differs from memalign only in that it (1) assigns the -// allocated memory to *pp rather than returning it, (2) fails and -// returns EINVAL if the alignment is not a power of two (3) fails and -// returns ENOMEM if memory cannot be allocated. -// -// @param rdi is void **pp -// @param rsi is size_t align -// @param rdx is size_t size -// @return eax -posix_memalign: - jmp *hook_posix_memalign(%rip) - .endfn posix_memalign,globl diff --git a/libc/mem/posix_memalign.c b/libc/mem/posix_memalign.c new file mode 100644 index 00000000000..a4c17377383 --- /dev/null +++ b/libc/mem/posix_memalign.c @@ -0,0 +1,58 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/macros.internal.h" +#include "libc/mem/mem.h" + +/** + * Allocates aligned memory, the POSIX way. + * + * Allocates a chunk of n bytes, aligned in accord with the alignment + * argument. Differs from memalign() only in that it: + * + * 1. Assigns the allocated memory to *pp rather than returning it + * 2. Fails and returns EINVAL if the alignment is not a power of two + * 3. Fails and returns ENOMEM if memory cannot be allocated + * + * @param pp receives pointer, only on success + * @param alignment must be 2-power multiple of sizeof(void *) + * @param bytes is number of bytes to allocate + * @return return 0 or EINVAL or ENOMEM w/o setting errno + * @see memalign() + */ +int posix_memalign(void **pp, size_t alignment, size_t bytes) { + int e; + void *m; + size_t q, r; + q = alignment / sizeof(void *); + r = alignment % sizeof(void *); + if (!r && q && IS2POW(q)) { + e = errno; + m = memalign(alignment, bytes); + errno = e; + if (m) { + *pp = m; + return 0; + } else { + return ENOMEM; + } + } else { + return EINVAL; + } +} diff --git a/libc/mem/putenv.c b/libc/mem/putenv.c index fbaea24d640..38da4a8fc8b 100644 --- a/libc/mem/putenv.c +++ b/libc/mem/putenv.c @@ -43,6 +43,12 @@ static void PutEnvInit(void) { atexit(PutEnvDestroy); } +void __freeenv(void *p) { + if (once) { + free(p); + } +} + int PutEnvImpl(char *s, bool overwrite) { char *p; unsigned i, namelen; @@ -62,7 +68,10 @@ int PutEnvImpl(char *s, bool overwrite) { goto replace; } } - if (i + 1 >= MAX_VARS) goto fail; + if (i + 1 >= MAX_VARS) { + free(s); + return enomem(); + } environ[i + 1] = NULL; replace: free(environ[i]); diff --git a/libc/mem/pvalloc.S b/libc/mem/pvalloc.S deleted file mode 100644 index 5f4dc422d27..00000000000 --- a/libc/mem/pvalloc.S +++ /dev/null @@ -1,30 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.internal.h" -#include "libc/notice.inc" -.source __FILE__ - -// Equivalent to valloc(minimum-page-that-holds(n)), that is, -// round up n to nearest pagesize. -// -// @param rdi is number of bytes needed -// @return rax is memory address, or NULL w/ errno -// @see dlpvalloc() -pvalloc:jmp *hook_pvalloc(%rip) - .endfn pvalloc,globl diff --git a/libc/mem/valloc.S b/libc/mem/pvalloc.c similarity index 77% rename from libc/mem/valloc.S rename to libc/mem/pvalloc.c index 8541aa7b7db..1c59640ad97 100644 --- a/libc/mem/valloc.S +++ b/libc/mem/pvalloc.c @@ -1,7 +1,7 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -17,13 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -#include "libc/notice.inc" -.source __FILE__ +#include "libc/mem/mem.h" -// Equivalent to memalign(4096, n). -// -// @param rdi is number of bytes needed -// @return rax is memory address, or NULL w/ errno -// @see dlvalloc() -valloc: jmp *hook_valloc(%rip) - .endfn valloc,globl +/** + * Equivalent to memalign(PAGESIZE, ROUNDUP(n, PAGESIZE)). + * + * @param n number of bytes needed + * @return memory address, or NULL w/ errno + * @see valloc() + */ +void *pvalloc(size_t n) { + return memalign(PAGESIZE, ROUNDUP(n, PAGESIZE)); +} diff --git a/libc/mem/unhexstr.c b/libc/mem/unhexstr.c index 2bbbe559241..f949a36fce9 100644 --- a/libc/mem/unhexstr.c +++ b/libc/mem/unhexstr.c @@ -23,6 +23,5 @@ nodiscard void *unhexstr(const char *hexdigs) { assert(strlen(hexdigs) % 2 == 0); - return unhexbuf(memalign(__BIGGEST_ALIGNMENT__, strlen(hexdigs) / 2), - strlen(hexdigs) / 2, hexdigs); + return unhexbuf(malloc(strlen(hexdigs) / 2), strlen(hexdigs) / 2, hexdigs); } diff --git a/libc/log/isterminalinarticulate.c b/libc/mem/valloc.c similarity index 83% rename from libc/log/isterminalinarticulate.c rename to libc/mem/valloc.c index ed57406af84..39467eaeb92 100644 --- a/libc/log/isterminalinarticulate.c +++ b/libc/mem/valloc.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,13 +16,15 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/safemacros.internal.h" -#include "libc/calls/calls.h" -#include "libc/log/log.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/sig.h" +#include "libc/mem/mem.h" -bool IsTerminalInarticulate(void) { - return !strcmp(nulltoempty(getenv("TERM")), "dumb"); +/** + * Equivalent to memalign(PAGESIZE, n). + * + * @param n number of bytes needed + * @return memory address, or NULL w/ errno + * @see pvalloc() + */ +void *valloc(size_t n) { + return memalign(PAGESIZE, n); } diff --git a/libc/runtime/g_argc.S b/libc/nexgen32e/argc.S similarity index 100% rename from libc/runtime/g_argc.S rename to libc/nexgen32e/argc.S diff --git a/libc/mem/hook/valloc.S b/libc/nexgen32e/envp.S similarity index 86% rename from libc/mem/hook/valloc.S rename to libc/nexgen32e/envp.S index a55cbf241bb..422c367471f 100644 --- a/libc/mem/hook/valloc.S +++ b/libc/nexgen32e/envp.S @@ -1,5 +1,5 @@ /*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ +│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ @@ -17,17 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.internal.h" -.source __FILE__ +#include "libc/notice.inc" - .initbss 202,_init_valloc -hook_valloc: - .quad 0 - .endobj hook_valloc,globl,hidden + .initbss 300,_init_envp +// Global variable holding _start(envp) parameter. +__envp: .quad 0 + .endobj __envp,globl .previous - .init.start 202,_init_valloc - .hidden dlvalloc - ezlea dlvalloc,ax + .init.start 300,_init_envp + mov %r14,%rax stosq - yoink free - .init.end 202,_init_valloc + .init.end 300,_init_envp diff --git a/libc/nexgen32e/gc.S b/libc/nexgen32e/gc.S index 782c59a909e..c098f646b17 100644 --- a/libc/nexgen32e/gc.S +++ b/libc/nexgen32e/gc.S @@ -32,6 +32,7 @@ // // @param rax,rdx,xmm0,xmm1,st0,st1 is return value // @see test/libc/runtime/gc_test.c + nop # backtrace workaround // __gc: decq __garbage(%rip) mov __garbage(%rip),%r8 diff --git a/libc/nexgen32e/llog10.S b/libc/nexgen32e/llog10.S deleted file mode 100644 index 869f0ca29db..00000000000 --- a/libc/nexgen32e/llog10.S +++ /dev/null @@ -1,66 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.internal.h" - -// Fast log₁₀ when 𝑥 is an integer. -// -// @param rdi is uint64 𝑥 -// @domain 0<𝑥<2⁶⁴ ∧ 𝑥∊ℤ -llog10: .leafprologue - .profilable - bsr %rdi,%rax - jz 3f - lea llog10data(%rip),%rdx - movsbl 1(%rdx,%rax),%eax - cmp 2f-1f(%rdx,%rax,8),%rdi - sbb $0,%al - jmp 4f -3: xor %eax,%eax # domain error -4: .leafepilogue - .endfn llog10,globl - - .rodata -llog10data: -1: .byte 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4 - .byte 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9 - .byte 9, 9,10,10,10,11,11,11,12,12,12,12,13,13,13,14 - .byte 14,14,15,15,15,15,16,16,16,17,17,17,18,18,18,18 - .byte 19,19,19 - .align 8 -2: .quad 0 - .quad 10 - .quad 100 - .quad 1000 - .quad 10000 - .quad 100000 - .quad 1000000 - .quad 10000000 - .quad 100000000 - .quad 1000000000 - .quad 10000000000 - .quad 100000000000 - .quad 1000000000000 - .quad 10000000000000 - .quad 100000000000000 - .quad 1000000000000000 - .quad 10000000000000000 - .quad 100000000000000000 - .endobj llog10data - .previous - .source __FILE__ diff --git a/libc/rand/rngset.c b/libc/rand/rngset.c index 9b4aba7a26b..2e399e06ac9 100644 --- a/libc/rand/rngset.c +++ b/libc/rand/rngset.c @@ -46,7 +46,7 @@ noasan void *rngset(void *b, size_t n, uint64_t seed(void), size_t reseed) { uint64_t i, x, t = 0; unsigned char *p = b; if (IsAsan()) { - __asan_check(b, n); + __asan_verify(b, n); } if (!seed) { t = reseed; diff --git a/libc/runtime/abort-nt.c b/libc/runtime/abort-nt.c index fd3425e5ea1..fc095cb6170 100644 --- a/libc/runtime/abort-nt.c +++ b/libc/runtime/abort-nt.c @@ -16,10 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/pushpop.h" #include "libc/calls/internal.h" -#include "libc/calls/struct/siginfo.h" -#include "libc/nt/enum/ctrlevent.h" +#include "libc/calls/typedef/sigaction_f.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/sig.h" diff --git a/libc/runtime/arememoryintervalsok.c b/libc/runtime/arememoryintervalsok.c index 3d973e728a6..67e78764359 100644 --- a/libc/runtime/arememoryintervalsok.c +++ b/libc/runtime/arememoryintervalsok.c @@ -19,6 +19,7 @@ #include "libc/runtime/memtrack.internal.h" noasan bool AreMemoryIntervalsOk(const struct MemoryIntervals *mm) { + /* asan runtime depends on this function */ int i; for (i = 0; i < mm->i; ++i) { if (mm->p[i].y < mm->p[i].x) { diff --git a/libc/runtime/cxaatexit.internal.h b/libc/runtime/cxaatexit.internal.h new file mode 100644 index 00000000000..bb7c17254b8 --- /dev/null +++ b/libc/runtime/cxaatexit.internal.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ +#define COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +#include "libc/stdio/stdio.h" + +struct CxaAtexitBlocks { + struct CxaAtexitBlock { + unsigned mask; + struct CxaAtexitBlock *next; + struct CxaAtexit { + void *fp; + void *arg; + void *pred; + } p[ATEXIT_MAX]; + } * p, root; +}; + +extern struct CxaAtexitBlocks __cxa_blocks; + +void __cxa_printexits(FILE *, void *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_RUNTIME_CXAATEXIT_H_ */ diff --git a/libc/runtime/describeframe.c b/libc/runtime/describeframe.c new file mode 100644 index 00000000000..0ef3f63f870 --- /dev/null +++ b/libc/runtime/describeframe.c @@ -0,0 +1,49 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/libfatal.internal.h" +#include "libc/macros.internal.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" + +#define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) +#define UNSHADOW(x) ((int64_t)(MAX(0, (x)-0x7fff8000)) << 3) +#define FRAME(x) ((int)((x) >> 16)) + +noasan const char *DescribeFrame(int x) { + /* asan runtime depends on this function */ + char *p; + static char buf[128]; + if (IsShadowFrame(x)) { + p = buf; + p = __stpcpy(p, " shadow of "); + p = __fixcpy(p, UNSHADOW(ADDR(x)), 48); + return buf; + return " shadow "; + } else if (IsAutoFrame(x)) { + return " automap"; + } else if (IsFixedFrame(x)) { + return " fixed "; + } else if (IsArenaFrame(x)) { + return " arena "; + } else if (IsStackFrame(x)) { + return " stack "; + } else { + return ""; + } +} diff --git a/libc/runtime/describemapping.c b/libc/runtime/describemapping.c new file mode 100644 index 00000000000..3dffeab43f9 --- /dev/null +++ b/libc/runtime/describemapping.c @@ -0,0 +1,40 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/memtrack.internal.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.h" + +noasan char *DescribeMapping(int prot, int flags, char p[hasatleast 8]) { + /* asan runtime depends on this function */ + p[0] = (prot & PROT_READ) ? 'r' : '-'; + p[1] = (prot & PROT_WRITE) ? 'w' : '-'; + p[2] = (prot & PROT_EXEC) ? 'x' : '-'; + if (flags & MAP_PRIVATE) { + p[3] = 'p'; + } else if (flags & MAP_SHARED) { + p[3] = 's'; + } else { + p[3] = '?'; + } + p[4] = (flags & MAP_ANONYMOUS) ? 'a' : 'f'; + p[5] = (flags & MAP_GROWSDOWN) ? 'S' : '-'; + p[6] = (flags & MAP_FIXED) ? 'F' : '-'; + p[7] = 0; + return p; +} diff --git a/libc/runtime/directmap-metal.c b/libc/runtime/directmap-metal.c index 3dbaebddd7d..d7d0550e4bf 100644 --- a/libc/runtime/directmap-metal.c +++ b/libc/runtime/directmap-metal.c @@ -29,6 +29,7 @@ static uint64_t sys_mmap_metal_break; noasan struct DirectMap sys_mmap_metal(void *paddr, size_t size, int prot, int flags, int fd, int64_t off) { + /* asan runtime depends on this function */ size_t i; struct mman *mm; struct DirectMap res; diff --git a/libc/runtime/directmap-nt.c b/libc/runtime/directmap-nt.c index c18cd853f47..00cc2a85c00 100644 --- a/libc/runtime/directmap-nt.c +++ b/libc/runtime/directmap-nt.c @@ -32,6 +32,7 @@ textwindows noasan struct DirectMap sys_mmap_nt(void *addr, size_t size, int prot, int flags, int64_t handle, int64_t off) { + /* asan runtime depends on this function */ uint32_t got; size_t i, upsize; struct DirectMap dm; diff --git a/libc/runtime/directmap.c b/libc/runtime/directmap.c index 878a4011437..ab1a121b8e9 100644 --- a/libc/runtime/directmap.c +++ b/libc/runtime/directmap.c @@ -18,8 +18,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" #include "libc/calls/sysdebug.internal.h" +#include "libc/errno.h" #include "libc/nt/runtime.h" #include "libc/runtime/directmap.internal.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/str/str.h" /** * Obtains memory mapping directly from system. @@ -31,11 +34,15 @@ */ noasan struct DirectMap sys_mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { + /* asan runtime depends on this function */ + char mode[8]; struct DirectMap dm; if (!IsWindows() && !IsMetal()) { dm.addr = __sys_mmap(addr, size, prot, flags, fd, off, off); - SYSDEBUG("sys_mmap(0x%x, 0x%x, %d, 0x%x, %d, %d) -> 0x%x", addr, size, prot, - flags, fd, off, dm.addr); + SYSDEBUG("sys_mmap(0x%p%s, 0x%x, %s, %d, %d) -> 0x%p %s", addr, + DescribeFrame((intptr_t)addr >> 16), size, + DescribeMapping(prot, flags, mode), (long)fd, off, dm.addr, + dm.addr != MAP_FAILED ? "" : strerror(errno)); dm.maphandle = kNtInvalidHandleValue; return dm; } else if (IsMetal()) { diff --git a/libc/runtime/ftrace-hook.S b/libc/runtime/ftrace-hook.S index 75e8f083ded..f17c1bb7a00 100644 --- a/libc/runtime/ftrace-hook.S +++ b/libc/runtime/ftrace-hook.S @@ -20,7 +20,7 @@ .privileged ftrace_hook: - cmp $0,ftrace(%rip) + cmp $0,g_ftrace(%rip) je 1f ret 1: push %rbp diff --git a/libc/runtime/ftrace.c b/libc/runtime/ftrace.c index c93ce1f8952..58f88f986e8 100644 --- a/libc/runtime/ftrace.c +++ b/libc/runtime/ftrace.c @@ -18,4 +18,4 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/runtime.h" -int ftrace; +int g_ftrace; diff --git a/libc/runtime/ftracer.c b/libc/runtime/ftracer.c index b8c15658fe7..013b724b247 100644 --- a/libc/runtime/ftracer.c +++ b/libc/runtime/ftracer.c @@ -24,9 +24,11 @@ #include "libc/nexgen32e/rdtscp.h" #include "libc/nexgen32e/stackframe.h" #include "libc/nexgen32e/x86feature.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #pragma weak stderr @@ -49,7 +51,7 @@ static int g_lastsymbol; static uint64_t laststamp; static struct SymbolTable *g_symbols; -static privileged noinstrument noasan int GetNestingLevelImpl( +static privileged noinstrument noasan noubsan int GetNestingLevelImpl( struct StackFrame *frame) { int nesting = -2; while (frame) { @@ -59,7 +61,7 @@ static privileged noinstrument noasan int GetNestingLevelImpl( return MAX(0, nesting); } -static privileged noinstrument noasan int GetNestingLevel( +static privileged noinstrument noasan noubsan int GetNestingLevel( struct StackFrame *frame) { int nesting; nesting = GetNestingLevelImpl(frame); @@ -75,7 +77,8 @@ static privileged noinstrument noasan int GetNestingLevel( * prologues of other functions. We assume those functions behave * according to the System Five NexGen32e ABI. */ -privileged noinstrument noasan void ftracer(void) { +privileged noinstrument noasan noubsan void ftracer(void) { + /* asan runtime depends on this function */ int symbol; uint64_t stamp; static bool noreentry; @@ -85,11 +88,11 @@ privileged noinstrument noasan void ftracer(void) { stamp = rdtsc(); frame = __builtin_frame_address(0); frame = frame->next; - if ((symbol = GetSymbol(g_symbols, frame->addr)) != -1 && + if ((symbol = __get_symbol(g_symbols, frame->addr)) != -1 && symbol != g_lastsymbol) { g_lastsymbol = symbol; __printf("+ %*s%s %d\r\n", GetNestingLevel(frame) * 2, "", - GetSymbolName(g_symbols, symbol), + __get_symbol_name(g_symbols, symbol), (long)(unsignedsubtract(stamp, laststamp) / 3.3)); laststamp = X86_HAVE(RDTSCP) ? rdtscp(0) : rdtsc(); } diff --git a/libc/log/getsymbol.c b/libc/runtime/getsymbol.c similarity index 77% rename from libc/log/getsymbol.c rename to libc/runtime/getsymbol.c index db4830125dc..a480051d009 100644 --- a/libc/log/getsymbol.c +++ b/libc/runtime/getsymbol.c @@ -16,14 +16,27 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/log.h" -#include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" -/** - * Returns name of symbol at address. - */ -char *GetSymbolByAddr(int64_t addr) { - struct SymbolTable *st = GetSymbolTable(); - return GetSymbolName(st, GetSymbol(st, addr)); +privileged noinstrument noasan noubsan int __get_symbol(struct SymbolTable *t, + intptr_t a) { + /* asan runtime depends on this function */ + unsigned l, m, r, n, k; + if (t) { + l = 0; + r = n = t->count; + k = a - t->addr_base; + while (l < r) { + m = (l + r) >> 1; + if (t->symbols[m].y < k) { + l = m + 1; + } else { + r = m; + } + } + if (l < n && t->symbols[l].x <= k && k <= t->symbols[l].y) { + return l; + } + } + return -1; } diff --git a/libc/runtime/getsymbolname.c b/libc/runtime/getsymbolname.c new file mode 100644 index 00000000000..4da3690d16e --- /dev/null +++ b/libc/runtime/getsymbolname.c @@ -0,0 +1,29 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/symbols.internal.h" + +privileged noinstrument noasan noubsan char *__get_symbol_name( + struct SymbolTable *t, int s) { + /* asan runtime depends on this function */ + if (t && s != -1) { + return t->name_base + t->names[s]; + } else { + return 0; + } +} diff --git a/libc/runtime/isheap.c b/libc/runtime/isheap.c index e1f0b0a177b..a532b4380db 100644 --- a/libc/runtime/isheap.c +++ b/libc/runtime/isheap.c @@ -27,15 +27,6 @@ * @assume stack memory isn't stored beneath %rsp (-mno-red-zone) */ noasan bool _isheap(void *p) { - int x, i; - uintptr_t rsp; - asm("mov\t%%rsp,%0" : "=r"(rsp)); - if (ROUNDDOWN(rsp, STACKSIZE) == ROUNDDOWN((intptr_t)p, STACKSIZE)) { - return false; - } else { - if ((intptr_t)p <= (intptr_t)_end) return false; - x = (intptr_t)p >> 16; - i = FindMemoryInterval(&_mmi, x); - return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; - } + return kAutomapStart <= (intptr_t)p && + (intptr_t)p < kAutomapStart + kAutomapSize; } diff --git a/libc/runtime/mapanon.c b/libc/runtime/mapanon.c index 417885fdcb6..f59f465855b 100644 --- a/libc/runtime/mapanon.c +++ b/libc/runtime/mapanon.c @@ -33,19 +33,32 @@ * mmap(NULL, mapsize, PROT_READ | PROT_WRITE, * MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); * - * Except it offers a small saving on code size. + * If mmap() fails, possibly because the parent process did this: + * + * if (!vfork()) { + * setrlimit(RLIMIT_AS, &(struct rlimit){maxbytes, maxbytes}); + * execv(prog, (char *const[]){prog, 0}); + * } + * wait(0); + * + * Then this function will call: + * + * __oom_hook(size); + * + * If it's linked. The LIBC_TESTLIB library provides an implementation, + * which can be installed as follows: + * + * int main() { + * InstallQuotaHandlers(); + * // ... + * } + * + * That is performed automatically for unit test executables. */ -void *mapanon(size_t size) { +noasan void *mapanon(size_t size) { + /* asan runtime depends on this function */ void *m; - if (!size || bsrl(size) >= 40) { - errno = EINVAL; - return MAP_FAILED; - } m = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (m == MAP_FAILED) { - if (weaken(__oom_hook)) { - weaken(__oom_hook)(size); - } - } + if (m == MAP_FAILED && weaken(__oom_hook)) weaken(__oom_hook)(size); return m; } diff --git a/libc/runtime/memtrack.c b/libc/runtime/memtrack.c index 64d52518b5d..5fbe3f251c2 100644 --- a/libc/runtime/memtrack.c +++ b/libc/runtime/memtrack.c @@ -37,6 +37,7 @@ static noasan void *MoveMemoryIntervals(struct MemoryInterval *d, const struct MemoryInterval *s, int n) { + /* asan runtime depends on this function */ int i; assert(n >= 0); if (d > s) { @@ -53,6 +54,7 @@ static noasan void *MoveMemoryIntervals(struct MemoryInterval *d, static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) { + /* asan runtime depends on this function */ assert(i >= 0); assert(i + n <= mm->i); MoveMemoryIntervals(mm->p + i, mm->p + i + n, mm->i - (i + n)); @@ -60,6 +62,7 @@ static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, } static noasan void MapNewMappingArray(struct MemoryIntervals *mm) { + /* asan runtime depends on this function */ void *a; int i, x, y, g; size_t n, m, f; @@ -97,10 +100,10 @@ static noasan void MapNewMappingArray(struct MemoryIntervals *mm) { } } } - flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED; + flags = MAP_ANONYMOUS | MAP_PRIVATE; prot = PROT_READ | PROT_WRITE; SYSDEBUG("MapNewMappingArray(0x%x, 0x%x) %d/%d/%d", a, m, i, mm->i, mm->n); - dm = sys_mmap(a, m, prot, flags, -1, 0); + dm = sys_mmap(a, m, prot, flags | MAP_FIXED, -1, 0); if ((p = dm.addr) != MAP_FAILED) { MoveMemoryIntervals(p, mm->p, i); MoveMemoryIntervals(p + i + 1, mm->p + i, mm->i - i); @@ -126,6 +129,7 @@ static noasan void MapNewMappingArray(struct MemoryIntervals *mm) { } noasan int CreateMemoryInterval(struct MemoryIntervals *mm, int i) { + /* asan runtime depends on this function */ int rc; rc = 0; assert(i >= 0); @@ -189,6 +193,7 @@ noasan int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, noasan int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, int prot, int flags) { + /* asan runtime depends on this function */ unsigned i; assert(y >= x); assert(AreMemoryIntervalsOk(mm)); diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 0090b0ffa3e..1a5608ccba7 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -4,12 +4,14 @@ #include "libc/macros.internal.h" #include "libc/nt/enum/version.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define _kAutomapStart 0x0000100080000000 /* asan can't spread its poison */ -#define _kAutomapSize 0x00000fff80000000 /* beyond the above mem address */ -#define _kFixedmapStart 0x0000200000000000 +#define _kAutomapStart 0x0000100080000000 +#define _kAutomapSize 0x00000fff80000000 +#define _kFixedmapStart 0x0000300000000000 +#define _kFixedmapSize 0x00000fff80000000 /* * TODO: Why can't we allocate addresses above 4GB on Windows 7 x64? @@ -18,8 +20,9 @@ COSMOPOLITAN_C_START_ #define MEMTRACK_ADDRESS(NORMAL, WIN7) \ (!(IsWindows() && NtGetVersion() < kNtVersionWindows10) ? NORMAL : WIN7) #define kAutomapStart MEMTRACK_ADDRESS(_kAutomapStart, 0x10000000) -#define kAutomapSize MEMTRACK_ADDRESS(_kAutomapSize, 0x40000000) -#define kFixedmapStart MEMTRACK_ADDRESS(_kFixedmapStart, 0x50000000) +#define kAutomapSize MEMTRACK_ADDRESS(_kAutomapSize, 0x30000000) +#define kFixedmapStart MEMTRACK_ADDRESS(_kFixedmapStart, 0x40000000) +#define kFixedmapSize MEMTRACK_ADDRESS(_kFixedmapSize, 0x30000000) struct MemoryInterval { int x; @@ -37,6 +40,9 @@ struct MemoryIntervals { extern hidden struct MemoryIntervals _mmi; +const char *DescribeFrame(int); +void PrintSystemMappings(int) hidden; +char *DescribeMapping(int, int, char[hasatleast 8]) hidden; bool AreMemoryIntervalsOk(const struct MemoryIntervals *) nosideeffect hidden; void PrintMemoryIntervals(int, const struct MemoryIntervals *) hidden; int TrackMemoryInterval(struct MemoryIntervals *, int, int, long, int, @@ -46,8 +52,70 @@ int ReleaseMemoryIntervals(struct MemoryIntervals *, int, int, void ReleaseMemoryNt(struct MemoryIntervals *, int, int) hidden; int UntrackMemoryIntervals(void *, size_t) hidden; -static inline noasan unsigned FindMemoryInterval( - const struct MemoryIntervals *mm, int x) { +forceinline pureconst bool IsAutoFrame(int x) { + return (kAutomapStart >> 16) <= x && + x <= ((kAutomapStart + (kAutomapSize - 1)) >> 16); +} + +forceinline pureconst bool IsArenaFrame(int x) { + return 0x5000 <= x && x < 0x7ffe; +} + +forceinline pureconst bool IsShadowFrame(int x) { + return 0x7fff <= x && x < 0x10008000; +} + +forceinline pureconst bool IsStackFrame(int x) { + return (GetStaticStackAddr(0) >> 16) <= x && + x <= ((GetStaticStackAddr(0) + (GetStackSize() - FRAMESIZE)) >> 16); +} + +forceinline pureconst bool IsFixedFrame(int x) { + return (kFixedmapStart >> 16) <= x && + x <= ((kFixedmapStart + (kFixedmapSize - 1)) >> 16); +} + +forceinline pureconst bool OverlapsImageSpace(const void *p, size_t n) { + const unsigned char *start, *ender; + if (n) { + start = p; + ender = start + (n - 1); + return ((_base <= start && start < _end) || + (_base <= ender && ender < _end) || + (start < _base && _end <= ender)); + } else { + return 0; + } +} + +forceinline pureconst bool OverlapsArenaSpace(const void *p, size_t n) { + intptr_t x, y; + if (n) { + x = (intptr_t)p; + y = x + (n - 1); + return ((0x50000000 <= x && x <= 0x7ffdffff) || + (0x50000000 <= y && y <= 0x7ffdffff) || + (x < 0x50000000 && 0x7ffdffff < y)); + } else { + return 0; + } +} + +forceinline pureconst bool OverlapsShadowSpace(const void *p, size_t n) { + intptr_t x, y; + if (n) { + x = (intptr_t)p; + y = x + (n - 1); + return ((0x7fff0000 <= x && x <= 0x10007fffffff) || + (0x7fff0000 <= y && y <= 0x10007fffffff) || + (x < 0x7fff0000 && 0x10007fffffff < y)); + } else { + return 0; + } +} + +forceinline unsigned FindMemoryInterval(const struct MemoryIntervals *mm, + int x) { unsigned l, m, r; l = 0; r = mm->i; @@ -62,6 +130,18 @@ static inline noasan unsigned FindMemoryInterval( return l; } +forceinline bool IsMemtracked(int x, int y) { + unsigned i; + i = FindMemoryInterval(&_mmi, x); + if (i == _mmi.i) return false; + if (!(_mmi.p[i].x <= x && x <= _mmi.p[i].y)) return false; + for (;;) { + if (y <= _mmi.p[i].y) return true; + if (++i == _mmi.i) return false; + if (_mmi.p[i].x != _mmi.p[i - 1].y + 1) return false; + } +} + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ */ diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index 11a1bbfce5e..0a850755ef5 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/likely.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" @@ -24,6 +25,7 @@ #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/log/backtrace.internal.h" +#include "libc/log/log.h" #include "libc/macros.internal.h" #include "libc/rand/rand.h" #include "libc/runtime/directmap.internal.h" @@ -34,83 +36,213 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" -#define IP(X) (intptr_t)(X) -#define VIP(X) (void *)IP(X) -#define ADDR(c) (void *)(IP(c) << 16) -#define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) -#define CANONICAL(p) (-0x800000000000 <= IP(p) && IP(p) <= 0x7fffffffffff) +#define IP(X) (intptr_t)(X) +#define VIP(X) (void *)IP(X) +#define SMALL(n) ((n) <= 0xffffffffffff) +#define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) +#define LEGAL(p) (-0x800000000000 <= IP(p) && IP(p) <= 0x7fffffffffff) +#define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) +#define SHADE(x) (((intptr_t)(x) >> 3) + 0x7fff8000) +#define FRAME(x) ((int)((intptr_t)(x) >> 16)) + +forceinline wontreturn void Die(void) { + if (weaken(__die)) weaken(__die)(); + abort(); +} + +noasan static bool IsMapped(char *p, size_t n) { + return OverlapsImageSpace(p, n) || IsMemtracked(FRAME(p), FRAME(p + (n - 1))); +} + +noasan static bool NeedAutomap(char *p, size_t n) { + return !p || OverlapsArenaSpace(p, n) || OverlapsShadowSpace(p, n) || + IsMapped(p, n); +} + +noasan static bool ChooseInterval(int x, int n, int *res) { + int i; + if (_mmi.i) { + i = FindMemoryInterval(&_mmi, x); + if (i < _mmi.i) { + if (x + n < _mmi.p[i].x) { + *res = x; + return true; + } + while (++i < _mmi.i) { + if (_mmi.p[i].x - _mmi.p[i - 1].y > n) { + *res = _mmi.p[i - 1].y + 1; + return true; + } + } + } + if (INT_MAX - _mmi.p[i - 1].y >= n) { + *res = _mmi.p[i - 1].y + 1; + return true; + } + return false; + } else { + *res = x; + return true; + } +} + +noasan static bool Automap(int n, int *res) { + *res = -1; + if (ChooseInterval(FRAME(kAutomapStart), n, res)) { + assert(*res >= FRAME(kAutomapStart)); + if (*res + n <= FRAME(kAutomapStart + (kAutomapStart - 1))) { + return true; + } else { + SYSDEBUG("mmap(0x%p, 0x%x) ENOMEM (automap interval exhausted)", + ADDR(*res), ADDR(n + 1)); + return false; + } + } else { + SYSDEBUG("mmap(0x%p, 0x%x) ENOMEM (automap failed)", ADDR(*res), + ADDR(n + 1)); + return false; + } +} /** - * Beseeches system for page-table entries. + * Beseeches system for page-table entries, e.g. * - * char *p = mmap(NULL, 65536, PROT_READ | PROT_WRITE, - * MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - * munmap(p, 65536); + * char *m; + * m = mmap(NULL, FRAMESIZE, PROT_READ | PROT_WRITE, + * MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + * munmap(m, FRAMESIZE); * - * @param addr optionally requests a particular virtual base address, - * which needs to be 64kb aligned if passed (for NT compatibility) - * @param size must be >0 and needn't be a multiple of FRAMESIZE - * @param prot can have PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE, etc. + * @param addr should be 0 to let your memory manager choose address; + * unless MAP_FIXED or MAP_FIXED_NOREPLACE are specified in flags + * in which case this function will do precicely as you ask, even + * if p=0 (in which you need -fno-delete-null-pointer-checks); it + * needs to be 64kb aligned because it's a wise choice that sadly + * needs to be made mandatory because of Windows although you can + * use __sys_mmap() to circumvent it on System Five in which case + * runtime support services, e.g. asan memory safety, could break + * @param size must be >0 and needn't be a multiple of FRAMESIZE, but + * will be rounded up to FRAMESIZE automatically if MAP_ANONYMOUS + * is specified + * @param prot can have PROT_READ/PROT_WRITE/PROT_EXEC/PROT_NONE/etc. * @param flags can have MAP_ANONYMOUS, MAP_SHARED, MAP_PRIVATE, etc. - * @param fd is an open()'d file descriptor whose contents shall be - * mapped, and is ignored if MAP_ANONYMOUS is specified + * @param fd is an open()'d file descriptor, whose contents shall be + * made available w/ automatic reading at the chosen address and + * must be -1 if MAP_ANONYMOUS is specified * @param off specifies absolute byte index of fd's file for mapping, * should be zero if MAP_ANONYMOUS is specified, and sadly needs * to be 64kb aligned too * @return virtual base address of new mapping, or MAP_FAILED w/ errno */ -void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { +noasan void *mmap(void *addr, size_t size, int prot, int flags, int fd, + int64_t off) { struct DirectMap dm; - int i, x, n, m, a, b, f; - SYSDEBUG("mmap(0x%x, 0x%x, %d, 0x%x, %d, %d)", addr, size, prot, flags, fd, - off); - if (!size) return VIP(einval()); - if (size > 0x0000010000000000ull) return VIP(enomem()); - if (!ALIGNED(off)) return VIP(einval()); - if (!ALIGNED(addr)) return VIP(einval()); - if (!CANONICAL(addr)) return VIP(einval()); - if (!(!!(flags & MAP_ANONYMOUS) ^ (fd != -1))) return VIP(einval()); - if (!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED))) return VIP(einval()); - if (fd == -1) size = ROUNDUP(size, FRAMESIZE); - if (IsWindows() && fd == -1) prot |= PROT_WRITE; - if (flags & MAP_FIXED) { - if (UntrackMemoryIntervals(addr, size) == -1) { - return MAP_FAILED; + int a, b, i, f, m, n, x; + char mode[8], *p = addr; + if (UNLIKELY(!size)) { + SYSDEBUG("mmap(0x%p, 0x%x) EINVAL (size=0)", p, size); + return VIP(einval()); + } + if (UNLIKELY(!SMALL(size))) { + SYSDEBUG("mmap(0x%p, 0x%x) EINVAL (size isn't 48-bit)", p, size); + return VIP(einval()); + } + if (UNLIKELY(!LEGAL(p))) { + SYSDEBUG("mmap(0x%p, 0x%x) EINVAL (p isn't 48-bit)", p, size); + return VIP(einval()); + } + if (UNLIKELY(!ALIGNED(p))) { + SYSDEBUG("mmap(0x%p, 0x%x) EINVAL (p isn't 64kb aligned)", p, size); + return VIP(einval()); + } + if (UNLIKELY(fd < -1)) { + SYSDEBUG("mmap(0x%p, 0x%x, fd=%d) EBADF", p, size, (long)fd); + return VIP(ebadf()); + } + if (UNLIKELY(!((fd != -1) ^ !!(flags & MAP_ANONYMOUS)))) { + SYSDEBUG("mmap(0x%p, 0x%x, %s, %d, %d) EINVAL (fd anonymous mismatch)", p, + size, DescribeMapping(prot, flags, mode), (long)fd, off); + return VIP(einval()); + } + if (UNLIKELY(!(!!(flags & MAP_PRIVATE) ^ !!(flags & MAP_SHARED)))) { + SYSDEBUG("mmap(0x%p, 0x%x) EINVAL (MAP_SHARED ^ MAP_PRIVATE)", p, size); + return VIP(einval()); + } + if (UNLIKELY(off < 0)) { + SYSDEBUG("mmap(0x%p, 0x%x, off=%d) EINVAL (neg off)", p, size, off); + return VIP(einval()); + } + if (UNLIKELY(INT64_MAX - size < off)) { + SYSDEBUG("mmap(0x%p, 0x%x, off=%d) EINVAL (too large)", p, size, off); + return VIP(einval()); + } + if (UNLIKELY(!ALIGNED(off))) { + SYSDEBUG("mmap(0x%p, 0x%x) EINVAL (p isn't 64kb aligned)", p, size); + return VIP(einval()); + } + if ((flags & MAP_FIXED_NOREPLACE) && IsMapped(p, size)) { + if (OverlapsImageSpace(p, size)) { + SYSDEBUG("mmap(0x%p, 0x%x) EFAULT (overlaps image)", p, size); + } else { + SYSDEBUG("mmap(0x%p, 0x%x) EFAULT (overlaps existing)", p, size); } - } else { - x = kAutomapStart >> 16; - n = ROUNDUP(size, FRAMESIZE) >> 16; - for (i = 0; i < _mmi.i; ++i) { - if (_mmi.p[i].y < x) continue; - if (__builtin_add_overflow(_mmi.p[i].y, n, &m)) return VIP(enomem()); - if (_mmi.p[i].x > x + n - 1) break; - x = _mmi.p[i].y + 1; + return VIP(efault()); + } + SYSDEBUG("mmap(0x%p, 0x%x, %s, %d, %d)", p, size, + DescribeMapping(prot, flags, mode), (long)fd, off); + if (fd == -1) { + size = ROUNDUP(size, FRAMESIZE); + if (IsWindows()) { + prot |= PROT_WRITE; /* kludge */ } - if (x + n - 1 >= ((kAutomapStart + kAutomapSize) >> 16)) { - return (void *)(intptr_t)enomem(); + } + n = FRAME(size) + !!(size & (FRAMESIZE - 1)); + f = (flags & ~MAP_FIXED_NOREPLACE) | MAP_FIXED; + if (flags & MAP_FIXED) { + x = FRAME(p); + if (IsWindows()) { + if (UntrackMemoryIntervals(p, size)) { + SYSDEBUG("FIXED UNTRACK FAILED %s", strerror(errno)); + Die(); + } } - addr = (void *)(intptr_t)((int64_t)x << 16); + } else if (!NeedAutomap(p, size)) { + x = FRAME(p); + } else if (!Automap(n, &x)) { + return VIP(enomem()); } - f = flags | MAP_FIXED; + p = (char *)ADDR(x); if (IsOpenbsd() && (f & MAP_GROWSDOWN)) { /* openbsd:dubstack */ - dm = sys_mmap(addr, size, prot, f & ~MAP_GROWSDOWN, fd, off); - if (dm.addr == MAP_FAILED) { - SYSDEBUG("sys_mmap failed"); - return MAP_FAILED; - } + dm = sys_mmap(p, size, prot, f & ~MAP_GROWSDOWN, fd, off); + if (dm.addr == MAP_FAILED) return MAP_FAILED; } - dm = sys_mmap(addr, size, prot, f, fd, off); - if (dm.addr == MAP_FAILED || dm.addr != addr) { - SYSDEBUG("sys_mmap failed"); + dm = sys_mmap(p, size, prot, f, fd, off); + if (UNLIKELY(dm.addr == MAP_FAILED)) { + if (IsWindows() && (flags & MAP_FIXED)) { + SYSDEBUG("mmap(0x%p, 0x%x) -> %s (%s)", p, size, strerror(errno), + "can't recover from MAP_FIXED errors on Windows"); + Die(); + } return MAP_FAILED; } - a = ROUNDDOWN((intptr_t)addr, FRAMESIZE) >> 16; - b = ROUNDDOWN((intptr_t)addr + size - 1, FRAMESIZE) >> 16; - if (TrackMemoryInterval(&_mmi, a, b, dm.maphandle, prot, flags) == -1) { - abort(); + if (UNLIKELY(dm.addr != p)) { + SYSDEBUG("KERNEL DIDN'T RESPECT MAP_FIXED"); + Die(); + } + if (!IsWindows() && (flags & MAP_FIXED)) { + if (UntrackMemoryIntervals(p, size)) { + SYSDEBUG("FIXED UNTRACK FAILED %s", strerror(errno)); + Die(); + } + } + if (TrackMemoryInterval(&_mmi, x, x + (n - 1), dm.maphandle, prot, flags)) { + if (sys_munmap(p, n) == -1) { + SYSDEBUG("TRACK MUNMAP FAILED %s", strerror(errno)); + Die(); + } + return MAP_FAILED; } - if (weaken(__asan_map_shadow)) { - weaken(__asan_map_shadow)((uintptr_t)dm.addr, size); + if (weaken(__asan_map_shadow) && !OverlapsShadowSpace(p, size)) { + weaken(__asan_map_shadow)((intptr_t)p, size); } - return dm.addr; + return p; } diff --git a/libc/runtime/mremap.c b/libc/runtime/mremap.c index e7fbd0b5b1e..c3fc40da27a 100644 --- a/libc/runtime/mremap.c +++ b/libc/runtime/mremap.c @@ -16,18 +16,52 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/bits/likely.h" +#include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" +#include "libc/nt/runtime.h" +#include "libc/runtime/directmap.internal.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/mremap.h" #include "libc/sysv/errfuns.h" #define IP(X) (intptr_t)(X) #define VIP(X) (void *)IP(X) +#define SMALL(n) ((n) <= 0xffffffffffff) #define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) +#define LEGAL(p) (-0x800000000000 <= IP(p) && IP(p) <= 0x7fffffffffff) +#define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) +#define SHADE(x) (((intptr_t)(x) >> 3) + 0x7fff8000) +#define FRAME(x) ((int)((intptr_t)(x) >> 16)) + +static size_t GetMapSize(size_t i, size_t *j) { + size_t n; + n = (size_t)(_mmi.p[i].y - _mmi.p[i].x + 1) << 16; + while (i + 1 < _mmi.i) { + if (_mmi.p[i + 1].x != _mmi.p[i].y + 1) break; + ++i; + n += (size_t)(_mmi.p[i].y - _mmi.p[i].x + 1) << 16; + } + *j = i; + return n; +} + +static bool MustMoveMap(intptr_t y, size_t j) { + return ADDR(_mmi.p[j].y) + FRAMESIZE > y || + (j + 1 < _mmi.i && ADDR(_mmi.p[j + 1].x) < y); +} /** - * Relocates mapping. + * Extends and/or relocates memory pages. * * @param p is old address * @param n is old size @@ -36,27 +70,134 @@ * @param q is new address */ void *mremap(void *p, size_t n, size_t m, int f, ... /* void *q */) { - return VIP(enosys()); /* TODO: Implement Me! */ + enosys(); + return MAP_FAILED; void *q; va_list va; - if (!IsWindows()) { - if (!n) return VIP(einval()); - if (!m) return VIP(einval()); - if (!ALIGNED(p)) return VIP(einval()); - n = ROUNDUP(n, FRAMESIZE); - m = ROUNDUP(m, FRAMESIZE); - if (f & MREMAP_FIXED) { - va_start(va, f); - q = va_arg(va, void *); - va_end(va); - if (!ALIGNED(q)) return VIP(einval()); + size_t i, j, k; + struct DirectMap dm; + int a, b, prot, flags; + if (UNLIKELY(!m)) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EINVAL (m=0)", p, n, m, f); + return VIP(einval()); + } + if (UNLIKELY(!n)) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EOPNOTSUPP (n=0)", p, n, m, f); + return VIP(eopnotsupp()); + } + if (UNLIKELY(!ALIGNED(n))) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EOPNOTSUPP (n align)", p, n, m, f); + return VIP(eopnotsupp()); + } + if (UNLIKELY(!ALIGNED(m))) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EOPNOTSUPP (n align)", p, n, m, f); + return VIP(eopnotsupp()); + } + if (UNLIKELY(!ALIGNED(p))) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EINVAL (64kb align)", p, n, m, f); + return VIP(einval()); + } + if (UNLIKELY(!SMALL(n))) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EINVAL (n too big)", p, n, m, f); + return VIP(enomem()); + } + if (UNLIKELY(!SMALL(m))) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EINVAL (m too big)", p, n, m, f); + return VIP(enomem()); + } + if (f & ~(MREMAP_MAYMOVE | MREMAP_FIXED)) { + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x) EINVAL (bad flag)", p, n, m, f); + return VIP(einval()); + } + if (!IsMemtracked(FRAME(p), FRAME((intptr_t)p + (n - 1)))) { + SYSDEBUG("munmap(0x%x, 0x%x) EFAULT (interval not tracked)", p, n); + return VIP(efault()); + } + SYSDEBUG("mremap(0x%p, 0x%x, 0x%x, 0x%x)", p, n, m, f); + i = FindMemoryInterval(&_mmi, FRAME(p)); + if (i >= _mmi.i) return VIP(efault()); + flags = _mmi.p[i].flags; + if (!(flags & MAP_ANONYMOUS)) { + return VIP(eopnotsupp()); /* TODO */ + } + if (f & MREMAP_FIXED) { + va_start(va, f); + q = va_arg(va, void *); + va_end(va); + if (!ALIGNED(q)) return VIP(einval()); + return VIP(eopnotsupp()); /* TODO */ + } + prot = _mmi.p[i].prot; + for (k = i + 1; k <= j; ++k) { + prot |= _mmi.p[k].prot; + if (_mmi.p[k].flags != flags) { + return VIP(enomem()); + } + } + if (m == n) { + return p; + } else if (m < n) { + if (munmap((char *)p + n, m - n) != -1) { + return p; } else { - q = NULL; - if (!(f & MREMAP_MAYMOVE)) { + return MAP_FAILED; + } + } else if (!MustMoveMap(j, (intptr_t)p + n)) { + dm = sys_mmap((char *)p + n, m - n, prot, flags | MAP_FIXED, -1, 0); + if (dm.addr == MAP_FAILED) return 0; + if (TrackMemoryInterval(&_mmi, ((uintptr_t)p + n) >> 16, + ((uintptr_t)p + m - FRAMESIZE) >> 16, dm.maphandle, + prot, flags) != -1) { + if (weaken(__asan_map_shadow)) { + weaken(__asan_map_shadow)((uintptr_t)dm.addr, m - n); } + return p; + } else { + abort(); + } + } else if (!(f & MREMAP_MAYMOVE)) { + return VIP(enomem()); + } else if (IsLinux()) { + a = (uint32_t)(kAutomapStart >> 16); + i = FindMemoryInterval(&_mmi, a); + if (i < _mmi.i) { + for (; i + 1 < _mmi.i; ++i) { + if (((size_t)(_mmi.p[i + 1].x - _mmi.p[i].y - 1) << 16) >= m) { + break; + } + } + if (__builtin_add_overflow(_mmi.p[i].y, 1, &a)) { + return VIP(enomem()); + } + } + if (__builtin_add_overflow(a, (int)((m >> 16) - 1), &b)) { + return VIP(enomem()); + } + q = sys_mremap((void *)p, n, m, MREMAP_MAYMOVE | MREMAP_FIXED, + (void *)ADDR(a)); + SYSDEBUG("sys_mremap(0x%p, 0x%x, 0x%x, 0x%x, 0x%x) -> 0x%p", p, n, m, + MREMAP_MAYMOVE | MREMAP_FIXED, ADDR(a)); + if (q == MAP_FAILED) return 0; + if (ReleaseMemoryIntervals(&_mmi, (uintptr_t)p >> 16, + ((uintptr_t)p + n - FRAMESIZE) >> 16, 0) != -1 && + TrackMemoryInterval(&_mmi, a, b, -1, prot, flags) != -1) { + if (weaken(__asan_poison)) { + if (!OverlapsShadowSpace(p, n)) { + weaken(__asan_poison)((intptr_t)p, n, kAsanUnmapped); + } + if (!OverlapsShadowSpace(q, m)) { + weaken(__asan_map_shadow)((intptr_t)q, m); + } + } + return (void *)ADDR(a); + } else { + abort(); } - return VIP(enosys()); + } else if ((q = mmap(0, m, prot, flags, -1, 0)) != MAP_FAILED) { + memcpy(q, p, n); + munmap(p, n); + return q; } else { - return VIP(enosys()); + return q; } } diff --git a/libc/runtime/munmap-metal.c b/libc/runtime/munmap-metal.c index fba4a6ce9fc..dea02e651f7 100644 --- a/libc/runtime/munmap-metal.c +++ b/libc/runtime/munmap-metal.c @@ -19,7 +19,7 @@ #include "libc/runtime/directmap.internal.h" #include "libc/runtime/pc.internal.h" -int sys_munmap_metal(void *addr, size_t size) { +noasan int sys_munmap_metal(void *addr, size_t size) { size_t i; uint64_t *e; struct mman *mm; diff --git a/libc/runtime/munmap-sysv.c b/libc/runtime/munmap-sysv.c new file mode 100644 index 00000000000..c4db57ac61f --- /dev/null +++ b/libc/runtime/munmap-sysv.c @@ -0,0 +1,34 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" +#include "libc/runtime/directmap.internal.h" +#include "libc/runtime/memtrack.internal.h" + +int sys_munmap(void *p, size_t n) { + int rc; + if (!IsMetal()) { + rc = __sys_munmap(p, n); + } else { + rc = sys_munmap_metal(p, n); + } + SYSDEBUG("sys_munmap(0x%p%s, 0x%x) -> %d", p, + DescribeFrame((intptr_t)p >> 16), n, (long)rc); + return rc; +} diff --git a/libc/runtime/munmap.c b/libc/runtime/munmap.c index 4d48010614a..2e0e4326ec6 100644 --- a/libc/runtime/munmap.c +++ b/libc/runtime/munmap.c @@ -16,42 +16,100 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/likely.h" #include "libc/calls/internal.h" #include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" +#include "libc/intrin/asan.internal.h" #include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/directmap.internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/str/str.h" #include "libc/sysv/errfuns.h" -#define IP(X) (intptr_t)(X) -#define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) -#define CANONICAL(p) (-0x800000000000 <= IP(p) && IP(p) <= 0x7fffffffffff) +#define IP(X) (intptr_t)(X) +#define SMALL(n) ((n) <= 0xffffffffffff) +#define ALIGNED(p) (!(IP(p) & (FRAMESIZE - 1))) +#define LEGAL(p) (-0x800000000000 <= IP(p) && IP(p) <= 0x7fffffffffff) +#define ADDR(x) ((int64_t)((uint64_t)(x) << 32) >> 16) +#define SHADE(x) (((intptr_t)(x) >> 3) + 0x7fff8000) +#define FRAME(x) ((int)((intptr_t)(x) >> 16)) /** * Releases memory pages. * - * This function may be used to punch holes in existing mappings, but your - * mileage may vary on Windows. + * This function may be used to punch holes in existing mappings, but + * your mileage may vary on Windows. * - * @param addr is a pointer within any memory mapped region the process + * @param p is a pointer within any memory mapped region the process * has permission to control, such as address ranges returned by * mmap(), the program image itself, etc. - * @param size is the amount of memory to unmap, which needs to be a + * @param n is the number of bytes to be unmapped, and needs to be a * multiple of FRAMESIZE for anonymous mappings, because windows * and for files size needs to be perfect to the byte bc openbsd * @return 0 on success, or -1 w/ errno */ -noasan int munmap(void *addr, size_t size) { +noasan int munmap(void *v, size_t n) { + /* asan runtime depends on this function */ int rc; - /* TODO(jart): are we unmapping shadows? */ - SYSDEBUG("munmap(0x%x, 0x%x)", addr, size); - if (!ALIGNED(addr) || !CANONICAL(addr) || !size) return einval(); - if (UntrackMemoryIntervals(addr, size) == -1) return -1; - if (IsWindows()) return 0; - if (IsMetal()) sys_munmap_metal(addr, size); - SYSDEBUG("sys_munmap(0x%x, 0x%x)", addr, size); - return sys_munmap(addr, size); + char poison, *p = v; + intptr_t a, b, x, y; + if (UNLIKELY(!n)) { + SYSDEBUG("munmap(0x%p, 0x%x) %s (n=0)", p, n); + return einval(); + } + if (UNLIKELY(!SMALL(n))) { + SYSDEBUG("munmap(0x%p, 0x%x) EINVAL (n isn't 48-bit)", p, n); + return einval(); + } + if (UNLIKELY(!LEGAL(p))) { + SYSDEBUG("munmap(0x%p, 0x%x) EINVAL (p isn't 48-bit)", p, n); + return einval(); + } + if (UNLIKELY(!LEGAL(p + (n - 1)))) { + SYSDEBUG("munmap(0x%p, 0x%x) EINVAL (p+(n-1) isn't 48-bit)", p, n); + return einval(); + } + if (UNLIKELY(!ALIGNED(p))) { + SYSDEBUG("munmap(0x%p, 0x%x) EINVAL (p isn't 64kb aligned)", p, n); + return einval(); + } + if (!IsMemtracked(FRAME(p), FRAME(p + (n - 1)))) { + SYSDEBUG("munmap(0x%p, 0x%x) EFAULT (interval not tracked)", p, n); + return efault(); + } + SYSDEBUG("munmap(0x%p, 0x%x)", p, n); + if (UntrackMemoryIntervals(p, n) != -1) { + if (!IsWindows()) { + rc = sys_munmap(p, n); + if (rc != -1) { + if (IsAsan() && !OverlapsShadowSpace(p, n)) { + a = SHADE(p); + b = a + (n >> 3); + if (IsMemtracked(FRAME(a), FRAME(b - 1))) { + x = ROUNDUP(a, FRAMESIZE); + y = ROUNDDOWN(b, FRAMESIZE); + if (x < y) { + /* delete shadowspace if unmapping ≥512kb */ + __repstosb((void *)a, kAsanUnmapped, x - a); + munmap((void *)x, y - x); + __repstosb((void *)y, kAsanUnmapped, b - y); + } else { + /* otherwise just poison and assume reuse */ + __repstosb((void *)a, kAsanUnmapped, b - a); + } + } else { + SYSDEBUG("unshadow(0x%x, 0x%x) EFAULT", a, b - a); + } + } + } + return rc; + } else { + return 0; /* UntrackMemoryIntervals does it for NT */ + } + } else { + return -1; + } } diff --git a/libc/runtime/opensymboltable.c b/libc/runtime/opensymboltable.c index f61f5ee18aa..6d2d229c494 100644 --- a/libc/runtime/opensymboltable.c +++ b/libc/runtime/opensymboltable.c @@ -20,11 +20,16 @@ #include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/calls/calls.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" #include "libc/elf/def.h" -#include "libc/elf/elf.h" +#include "libc/elf/scalar.h" +#include "libc/elf/struct/phdr.h" +#include "libc/elf/struct/shdr.h" +#include "libc/elf/struct/sym.h" #include "libc/errno.h" #include "libc/limits.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" @@ -35,12 +40,72 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" +#define GetStr(tab, rva) ((char *)(tab) + (rva)) +#define GetSection(e, s) ((void *)((intptr_t)(e) + (size_t)(s)->sh_offset)) +#define GetShstrtab(e) GetSection(e, GetShdr(e, (e)->e_shstrndx)) +#define GetSectionName(e, s) GetStr(GetShstrtab(e), (s)->sh_name) +#define GetPhdr(e, i) \ + ((Elf64_Phdr *)((intptr_t)(e) + (e)->e_phoff + \ + (size_t)(e)->e_phentsize * (i))) +#define GetShdr(e, i) \ + ((Elf64_Shdr *)((intptr_t)(e) + (e)->e_shoff + \ + (size_t)(e)->e_shentsize * (i))) + +noasan static char *GetStrtab(Elf64_Ehdr *e, size_t *n) { + char *name; + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = 0; i < e->e_shnum; ++i) { + shdr = GetShdr(e, i); + if (shdr->sh_type == SHT_STRTAB) { + name = GetSectionName(e, GetShdr(e, i)); + if (name && !__strcmp(name, ".strtab")) { + if (n) *n = shdr->sh_size; + return GetSection(e, shdr); + } + } + } + return 0; +} + +noasan static Elf64_Sym *GetSymtab(Elf64_Ehdr *e, Elf64_Xword *n) { + Elf64_Half i; + Elf64_Shdr *shdr; + for (i = e->e_shnum; i > 0; --i) { + shdr = GetShdr(e, i - 1); + if (shdr->sh_type == SHT_SYMTAB) { + if (shdr->sh_entsize != sizeof(Elf64_Sym)) continue; + if (n) *n = shdr->sh_size / shdr->sh_entsize; + return GetSection(e, shdr); + } + } + return 0; +} + +noasan static void GetImageRange(Elf64_Ehdr *elf, intptr_t *x, intptr_t *y) { + unsigned i; + Elf64_Phdr *phdr; + intptr_t start, end, pstart, pend; + start = INTPTR_MAX; + end = 0; + for (i = 0; i < elf->e_phnum; ++i) { + phdr = GetPhdr(elf, i); + if (phdr->p_type != PT_LOAD) continue; + pstart = phdr->p_vaddr; + pend = phdr->p_vaddr + phdr->p_memsz; + if (pstart < start) start = pstart; + if (pend > end) end = pend; + } + if (x) *x = start; + if (y) *y = end; +} + /** * Maps debuggable binary into memory and indexes symbol addresses. * * @return object freeable with CloseSymbolTable(), or NULL w/ errno */ -struct SymbolTable *OpenSymbolTable(const char *filename) { +noasan struct SymbolTable *OpenSymbolTable(const char *filename) { int fd; void *map; long *stp; @@ -60,8 +125,8 @@ struct SymbolTable *OpenSymbolTable(const char *filename) { elf = map = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) goto SystemError; if (READ32LE(map) != READ32LE("\177ELF")) goto RaiseEnoexec; - if (!(name_base = GetElfStrs(map, st.st_size, &m))) goto RaiseEnobufs; - if (!(symtab = GetElfSymbolTable(map, st.st_size, &n))) goto RaiseEnobufs; + if (!(name_base = GetStrtab(map, &m))) goto RaiseEnobufs; + if (!(symtab = GetSymtab(map, &n))) goto RaiseEnobufs; tsz = 0; tsz += sizeof(struct SymbolTable); tsz += sizeof(struct Symbol) * n; @@ -78,7 +143,7 @@ struct SymbolTable *OpenSymbolTable(const char *filename) { t->mapsize = tsz; t->names = (unsigned *)((char *)t + names_offset); t->name_base = (char *)((char *)t + name_base_offset); - GetElfVirtualAddressRange(elf, st.st_size, &t->addr_base, &t->addr_end); + GetImageRange(elf, &t->addr_base, &t->addr_end); memcpy(t->name_base, name_base, m); --t->addr_end; stp = (long *)((char *)t + stp_offset); @@ -122,6 +187,7 @@ struct SymbolTable *OpenSymbolTable(const char *filename) { RaiseEnoexec: errno = ENOEXEC; SystemError: + SYSDEBUG("OpenSymbolTable() %s", strerror(errno)); if (map != MAP_FAILED) { munmap(map, st.st_size); } diff --git a/libc/runtime/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c index 424578f9b43..7d419900e62 100644 --- a/libc/runtime/printmemoryintervals.c +++ b/libc/runtime/printmemoryintervals.c @@ -16,27 +16,45 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/log/log.h" +#include "libc/fmt/itoa.h" +#include "libc/log/libfatal.internal.h" +#include "libc/macros.internal.h" #include "libc/runtime/memtrack.internal.h" +#define ADDR(x) ((intptr_t)((int64_t)((uint64_t)(x) << 32) >> 16)) + +static bool IsNoteworthyHole(unsigned i, const struct MemoryIntervals *mm) { + // gaps between shadow frames aren't interesting + // the chasm from heap to stack ruins statistics + return !((IsArenaFrame(mm->p[i].y) && !IsArenaFrame(mm->p[i + 1].x)) || + (IsShadowFrame(mm->p[i].y) || IsShadowFrame(mm->p[i + 1].x)) || + (!IsStackFrame(mm->p[i].y) && IsStackFrame(mm->p[i + 1].x))); +} + void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) { - int i, frames, maptally, gaptally; - maptally = 0; - gaptally = 0; - (dprintf)(fd, "%s%zd%s\r\n", "mm->i == ", mm->i, ";"); + char *p, mode[8]; + long i, w, frames, maptally = 0, gaptally = 0; + for (w = i = 0; i < mm->i; ++i) { + w = MAX(w, LengthInt64Thousands(mm->p[i].y + 1 - mm->p[i].x)); + } for (i = 0; i < mm->i; ++i) { - if (i && mm->p[i].x != mm->p[i - 1].y + 1) { - frames = mm->p[i].x - mm->p[i - 1].y - 1; - gaptally += frames; - (dprintf)(fd, "%s%,zd%s\r\n", "/* ", frames, " */"); - } frames = mm->p[i].y + 1 - mm->p[i].x; maptally += frames; - (dprintf)(fd, "%s%3u%s0x%08x,0x%08x,%ld,%d,%d%s%,zd%s\r\n", "mm->p[", i, - "]=={", mm->p[i].x, mm->p[i].y, mm->p[i].h, mm->p[i].prot, - mm->p[i].flags, "}; /* ", frames, " */"); + __printf("%0*x-%0*x %s %,*dx%s", 12, ADDR(mm->p[i].x), 12, + ADDR(mm->p[i].y + 1), + DescribeMapping(mm->p[i].prot, mm->p[i].flags, mode), w, frames, + DescribeFrame(mm->p[i].x)); + if (i + 1 < _mmi.i) { + frames = mm->p[i + 1].x - mm->p[i].y - 1; + if (frames && IsNoteworthyHole(i, mm)) { + gaptally += frames; + __printf(" w/ %,d frame hole", frames); + } + } + if (mm->p[i].h != -1) { + __printf(" h=%d", mm->p[i].h); + } + __printf("\r\n"); } - (dprintf)(fd, "%s%,zd%s%,zd%s\r\n\r\n", "/* ", maptally, " frames mapped w/ ", - gaptally, " frames gapped */"); + __printf("# %d frames mapped w/ %,d frames gapped\r\n", maptally, gaptally); } diff --git a/libc/runtime/program_executable_name.c b/libc/runtime/program_executable_name.c index 5ba9292c824..1dbd1cfedac 100644 --- a/libc/runtime/program_executable_name.c +++ b/libc/runtime/program_executable_name.c @@ -20,7 +20,9 @@ #include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sysdebug.internal.h" #include "libc/dce.h" +#include "libc/log/libfatal.internal.h" #include "libc/macros.internal.h" #include "libc/mem/alloca.h" #include "libc/nt/runtime.h" @@ -32,6 +34,7 @@ #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/ok.h" +#define SIZE 1024 #define CTL_KERN 1 #define KERN_PROC 14 #define KERN_PROC_PATHNAME_FREEBSD 12 @@ -44,9 +47,9 @@ * guaranteed to exist, except on XNU and OpenBSD. It may be a symlink. * It may be spoofed. */ -char program_executable_name[1024]; +char program_executable_name[SIZE]; -static textwindows bool GetNtExePath(void) { +static textwindows bool GetNtExePath(char executable[SIZE]) { uint64_t w; wint_t x, y; uint32_t i, j; @@ -60,44 +63,38 @@ static textwindows bool GetNtExePath(void) { if (x == '\\') x = '/'; w = tpenc(x); do { - program_executable_name[j] = w; - if (++j == sizeof(program_executable_name)) { + executable[j] = w; + if (++j == SIZE) { return false; } } while ((w >>= 8)); } - program_executable_name[j] = 0; + executable[j] = 0; return true; } -textstartup void program_executable_name_init(int argc, char **argv, - char **envp, intptr_t *auxv) { +static textstartup void GetProgramExecutableName(char executable[SIZE], + char *p) { + char *t; size_t m; ssize_t n; int cmd[4]; - char *p, *t; - static bool once; - if (!cmpxchg(&once, 0, 1)) return; - if (IsWindows() && GetNtExePath()) return; + if (IsWindows() && GetNtExePath(executable)) return; n = 0; - p = argv[0]; if (fileexists(p)) { if (!_isabspath(p)) { - if (getcwd(program_executable_name, - sizeof(program_executable_name) - 1)) { - n = strlen(program_executable_name); - program_executable_name[n++] = '/'; + if (getcwd(executable, SIZE - 1)) { + n = strlen(executable); + executable[n++] = '/'; } } - } else if ((n = sys_readlinkat(AT_FDCWD, "/proc/self/exe", - program_executable_name, - sizeof(program_executable_name) - 1)) > 0) { - program_executable_name[n] = 0; + } else if ((n = sys_readlinkat(AT_FDCWD, "/proc/self/exe", executable, + SIZE - 1)) > 0) { + executable[n] = 0; return; - } else if ((n = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", - program_executable_name, - sizeof(program_executable_name) - 1)) > 0) { - program_executable_name[n] = 0; + } else if ((n = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", executable, + SIZE - 1)) > 0) { + executable[n] = 0; return; } else if (IsFreebsd() || IsNetbsd()) { cmd[0] = CTL_KERN; @@ -108,17 +105,28 @@ textstartup void program_executable_name_init(int argc, char **argv, cmd[2] = KERN_PROC_PATHNAME_NETBSD; } cmd[3] = -1; - m = sizeof(program_executable_name); - if (sysctl(cmd, ARRAYLEN(cmd), program_executable_name, &m, 0, 0) != -1) { + m = SIZE; + if (sysctl(cmd, ARRAYLEN(cmd), executable, &m, 0, 0) != -1) { return; } } for (; *p; ++p) { - if (n + 1 < sizeof(program_executable_name)) { - program_executable_name[n++] = *p; + if (n + 1 < SIZE) { + executable[n++] = *p; } } - program_executable_name[n] = 0; + executable[n] = 0; +} + +textstartup void program_executable_name_init(int argc, char **argv, + char **envp, intptr_t *auxv) { + static bool once; + char executable[SIZE]; + if (!cmpxchg(&once, 0, 1)) return; + __stpcpy(program_executable_name, argv[0]); + GetProgramExecutableName(executable, argv[0]); + __stpcpy(program_executable_name, executable); + SYSDEBUG("GetProgramExecutableName() -> %s", program_executable_name); } const void *const program_executable_name_init_ctor[] initarray = { diff --git a/libc/runtime/quick_exit.c b/libc/runtime/quick_exit.c new file mode 100644 index 00000000000..aea08672cb6 --- /dev/null +++ b/libc/runtime/quick_exit.c @@ -0,0 +1,49 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/pushpop.h" +#include "libc/bits/weaken.h" +#include "libc/nt/console.h" +#include "libc/nt/enum/consolemodeflags.h" +#include "libc/nt/runtime.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" + +/** + * Exits process faster. + * + * @param exitcode is masked with 255 + * @noreturn + */ +wontreturn void quick_exit(int exitcode) { + const uintptr_t *p; + if (weaken(fflush)) { + weaken(fflush)(0); + } + for (p = __fini_array_end; p > __fini_array_start;) { + ((void (*)(void))(*--p))(); + } + if (SupportsWindows() && __ntconsolemode) { + SetConsoleMode(GetStdHandle(pushpop(kNtStdInputHandle)), __ntconsolemode); + SetConsoleMode(GetStdHandle(pushpop(kNtStdOutputHandle)), + kNtEnableProcessedOutput | kNtEnableWrapAtEolOutput | + kNtEnableVirtualTerminalProcessing); + } + _Exit(exitcode); +} diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index c9c5344b407..0bc82514da4 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -8,14 +8,15 @@ COSMOPOLITAN_C_START_ typedef long jmp_buf[8] forcealign(CACHELINE); -extern int __argc; /* CRT */ -extern char **__argv; /* CRT */ extern char **environ; /* CRT */ -extern unsigned long *__auxv; /* CRT */ +extern const int __argc; /* CRT */ +extern char **const __argv; /* CRT */ +extern char **const __envp; /* CRT */ +extern unsigned long *const __auxv; /* CRT */ extern char program_executable_name[]; /* RII */ extern char *program_invocation_name; /* RII */ extern char *program_invocation_short_name; /* RII */ -extern int ftrace; /* CRT */ +extern int g_ftrace; /* CRT */ extern uint64_t g_syscount; /* RII */ extern const uint64_t kStartTsc; /* RII */ extern const char kTmpPath[]; /* RII */ @@ -46,6 +47,7 @@ void _longjmp(jmp_buf, int) libcesque wontreturn paramsnonnull(); void exit(int) wontreturn; void _exit(int) libcesque wontreturn; void _Exit(int) libcesque wontreturn; +void quick_exit(int) wontreturn; void abort(void) wontreturn noinstrument; int __cxa_atexit(void *, void *, void *) libcesque; int atfork(void *, void *) libcesque; @@ -81,7 +83,7 @@ int acct(const char *); void longsort(long *, size_t); bool _isheap(void *); -int NtGetVersion(void); +int NtGetVersion(void) pureconst; long missingno(); void __oom_hook(size_t); void __print(const void *, size_t); diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h new file mode 100644 index 00000000000..4384e49ab83 --- /dev/null +++ b/libc/runtime/stack.h @@ -0,0 +1,79 @@ +#ifndef COSMOPOLITAN_LIBC_RUNTIME_STACK_H_ +#define COSMOPOLITAN_LIBC_RUNTIME_STACK_H_ +#include "ape/config.h" +#include "libc/dce.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +/** + * Tunes APE stack maximum size. + * + * This defaults to `STACKSIZE`. The bottom-most page will be protected + * to ensure your stack does not magically grow beyond this value. It's + * possible to detect stack overflows, by calling `ShowCrashReports()`. + * + * If you want to know how much stack your programs needs, then + * + * STATIC_YOINK("stack_usage_logging"); + * + * will install an atexit() handler that appends to `o/$MODE/stack.log` + * + * @see libc/sysv/systemfive.S + * @see ape/ape.lds + */ +#define STATIC_STACK_SIZE(BYTES) \ + STATIC_SYMBOL("ape_stack_memsz", _STACK_STRINGIFY(BYTES) _STACK_EXTRA) + +/** + * Tunes APE stack virtual address. + * + * This defaults to `0x700000000000 - STACKSIZE`. The value defined by + * this macro will be respected, with two exceptions: (1) in MODE=tiny + * the operating system provided stack is used instead and (2) Windows + * Seven doesn't support 64-bit addresses so 0x10000000 - GetStackSize + * is used instead. + * + * @see libc/sysv/systemfive.S + * @see libc/nt/winmain.greg.c + * @see ape/ape.lds + */ +#define STATIC_STACK_ADDR(ADDR) \ + STATIC_SYMBOL("ape_stack_vaddr", _STACK_STRINGIFY(ADDR)) + +#define _STACK_STRINGIFY(ADDR) #ADDR + +#if IsAsan() +#define _STACK_EXTRA "*2" +#else +#define _STACK_EXTRA "" +#endif + +#if defined(__GNUC__) && defined(__ELF__) +COSMOPOLITAN_C_START_ + +extern char ape_stack_memsz[] __attribute__((__weak__)); + +#define GetStackSize() ((uintptr_t)ape_stack_memsz) + +/** + * Returns address of bottom of stack. + */ +#define GetStackAddr(ADDEND) \ + (((intptr_t)__builtin_frame_address(0) & -GetStackSize()) + (ADDEND)) + +/** + * Returns preferred bottom address of stack. + */ +#define GetStaticStackAddr(ADDEND) \ + ({ \ + intptr_t vAddr = 0; \ + asm(".weak\tape_stack_vaddr\n\t" \ + "movabs\t%1+ape_stack_vaddr,%0" \ + : "=r"(vAddr) \ + : "i"(ADDEND)); \ + vAddr; \ + }) + +COSMOPOLITAN_C_END_ +#endif /* GNU ELF */ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_RUNTIME_STACK_H_ */ diff --git a/libc/runtime/stackuse.c b/libc/runtime/stackuse.c new file mode 100644 index 00000000000..9e7138ae0cc --- /dev/null +++ b/libc/runtime/stackuse.c @@ -0,0 +1,80 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/dce.h" +#include "libc/fmt/itoa.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" + +static char stacklog[1024]; + +static size_t NullLength(const char *s) { + typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16))); + size_t n; + xmm_t v, z = {0}; + unsigned m, k = (uintptr_t)s & 15; + const xmm_t *p = (const xmm_t *)((uintptr_t)s & -16); + m = (__builtin_ia32_pmovmskb128(*p == z) ^ 0xffff) >> k << k; + while (!m) m = __builtin_ia32_pmovmskb128(*++p == z) ^ 0xffff; + n = (const char *)p + __builtin_ctzl(m) - s; + return n; +} + +static textexit void LogStackUse(void) { + int i, fd; + bool quote; + char *p, *q; + size_t n, usage; + const char *stack; + fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY, 0644); + stack = (char *)GetStackAddr(0); + usage = GetStackSize() - (NullLength(stack + PAGESIZE) + PAGESIZE); + p = FormatUint64(stacklog, usage); + for (i = 0; i < __argc; ++i) { + n = strlen(__argv[i]); + if ((q = memchr(__argv[i], '\n', n))) n = q - __argv[i]; + if (p - stacklog + 1 + 1 + n + 1 + 1 < sizeof(stacklog)) { + quote = !!memchr(__argv[i], ' ', n); + *p++ = ' '; + if (quote) *p++ = '\''; + p = mempcpy(p, __argv[i], n); + if (quote) *p++ = '\''; + } else { + break; + } + } + *p++ = '\n'; + write(fd, stacklog, p - stacklog); + close(fd); +} + +static textstartup void LogStackUseInit(void) { + if (IsTiny()) return; + if (isdirectory("o/" MODE) && + getcwd(stacklog, sizeof(stacklog) - strlen("/o/" MODE "/stack.log"))) { + strcat(stacklog, "/o/" MODE "/stack.log"); + atexit(LogStackUse); + } +} + +const void *const stack_usage_logging[] initarray = { + LogStackUseInit, +}; diff --git a/libc/runtime/symbols.internal.h b/libc/runtime/symbols.internal.h index a4400d084cf..6ee2ed1d534 100644 --- a/libc/runtime/symbols.internal.h +++ b/libc/runtime/symbols.internal.h @@ -1,8 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_SYMBOLS_H_ #define COSMOPOLITAN_LIBC_SYMBOLS_H_ -#include "libc/assert.h" -#include "libc/elf/elf.h" -#include "libc/runtime/ezmap.internal.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -27,35 +24,8 @@ const char *FindDebugBinary(void); struct SymbolTable *OpenSymbolTable(const char *); int CloseSymbolTable(struct SymbolTable **); int __hook(void *, struct SymbolTable *); - -forceinline int GetSymbol(struct SymbolTable *t, intptr_t a) { - unsigned l, m, r, n, k; - if (t) { - l = 0; - r = n = t->count; - k = a - t->addr_base; - while (l < r) { - m = (l + r) >> 1; - if (t->symbols[m].y < k) { - l = m + 1; - } else { - r = m; - } - } - if (l < n && t->symbols[l].x <= k && k <= t->symbols[l].y) { - return l; - } - } - return -1; -} - -forceinline char *GetSymbolName(struct SymbolTable *t, int s) { - if (t && s != -1) { - return t->name_base + t->names[s]; - } else { - return 0; - } -} +int __get_symbol(struct SymbolTable *, intptr_t); +char *__get_symbol_name(struct SymbolTable *, int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index b59ed8a5df6..c737a248406 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -89,12 +89,12 @@ static noasan textwindows noinstrument void MakeLongDoubleLongAgain(void) { static noasan textwindows wontreturn noinstrument void WinMainNew(void) { int64_t h; int version; - size_t size; int i, count; - uint64_t addr; int64_t inhand; struct WinArgs *wa; const char16_t *env16; + intptr_t stackaddr, allocaddr; + size_t allocsize, argsize, stacksize; extern char os asm("__hostos"); os = WINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */ version = NtGetPeb()->OSMajorVersion; @@ -115,19 +115,23 @@ static noasan textwindows wontreturn noinstrument void WinMainNew(void) { } _mmi.p = _mmi.s; _mmi.n = OPEN_MAX; - addr = version < 10 ? 0x10000000 - STACKSIZE : 0x777000000000; - size = ROUNDUP(STACKSIZE + sizeof(struct WinArgs), FRAMESIZE); - MapViewOfFileExNuma((_mmi.p[0].h = CreateFileMappingNuma( - -1, &kNtIsInheritable, kNtPageExecuteReadwrite, - size >> 32, size, NULL, kNtNumaNoPreferredNode)), - kNtFileMapWrite | kNtFileMapExecute, 0, 0, size, - (void *)addr, kNtNumaNoPreferredNode); - _mmi.p[0].x = addr >> 16; - _mmi.p[0].y = (addr >> 16) + ((size >> 16) - 1); + argsize = ROUNDUP(sizeof(struct WinArgs), FRAMESIZE); + stackaddr = version < 10 ? 0x10000000 : GetStaticStackAddr(0); + stacksize = GetStackSize(); + allocsize = argsize + stacksize; + allocaddr = stackaddr - argsize; + MapViewOfFileExNuma( + (_mmi.p[0].h = CreateFileMappingNuma( + -1, &kNtIsInheritable, kNtPageExecuteReadwrite, allocsize >> 32, + allocsize, NULL, kNtNumaNoPreferredNode)), + kNtFileMapWrite | kNtFileMapExecute, 0, 0, allocsize, (void *)allocaddr, + kNtNumaNoPreferredNode); + _mmi.p[0].x = allocaddr >> 16; + _mmi.p[0].y = (allocaddr >> 16) + ((allocsize >> 16) - 1); _mmi.p[0].prot = PROT_READ | PROT_WRITE | PROT_EXEC; _mmi.p[0].flags = MAP_PRIVATE | MAP_ANONYMOUS; _mmi.i = 1; - wa = (struct WinArgs *)(addr + size - sizeof(struct WinArgs)); + wa = (struct WinArgs *)allocaddr; count = GetDosArgv(GetCommandLine(), wa->argblock, ARRAYLEN(wa->argblock), wa->argv, ARRAYLEN(wa->argv)); for (i = 0; wa->argv[0][i]; ++i) { @@ -141,7 +145,7 @@ static noasan textwindows wontreturn noinstrument void WinMainNew(void) { FreeEnvironmentStrings(env16); wa->auxv[0][0] = pushpop(AT_EXECFN); wa->auxv[0][1] = (intptr_t)wa->argv[0]; - _jmpstack((char *)addr + STACKSIZE, cosmo, count, wa->argv, wa->envp, + _jmpstack((char *)stackaddr + stacksize, cosmo, count, wa->argv, wa->envp, wa->auxv); } diff --git a/libc/sock/mapdoserrortoerrno.c b/libc/sock/mapdoserrortoerrno.c deleted file mode 100644 index ea901d5d7a0..00000000000 --- a/libc/sock/mapdoserrortoerrno.c +++ /dev/null @@ -1,175 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ Permission to use, copy, modify, and/or distribute this software for │ -│ any purpose with or without fee is hereby granted, provided that the │ -│ above copyright notice and this permission notice appear in all copies. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ -│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ -│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ -│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ -│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ -│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ -│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ -│ PERFORMANCE OF THIS SOFTWARE. │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/errno.h" -#include "libc/nt/errors.h" -#include "libc/sock/sock.h" - -/** - * Translates Windows error using superset of consts.sh. - */ -textwindows errno_t __dos2errno(uint32_t error) { - switch (error) { - case kNtErrorModNotFound: - return ENOSYS; - case kNtErrorBadCommand: - return EACCES; - case kNtErrorBadLength: - return EACCES; - case kNtErrorBadNetpath: - return ENOENT; - case kNtErrorBadNetName: - return ENOENT; - case kNtErrorBadNetResp: - return ENETDOWN; - case kNtErrorBadPathname: - return ENOENT; - case kNtErrorCannotMake: - return EACCES; - case kNtErrorCommitmentLimit: - return ENOMEM; - case kNtErrorConnectionAborted: - return ECONNABORTED; - case kNtErrorConnectionActive: - return EISCONN; - case kNtErrorConnectionRefused: - return ECONNREFUSED; - case kNtErrorCrc: - return EACCES; - case kNtErrorDirNotEmpty: - return ENOTEMPTY; - case kNtErrorDupName: - return EADDRINUSE; - case kNtErrorFilenameExcedRange: - return ENOENT; - case kNtErrorFileNotFound: - return ENOENT; - case kNtErrorGenFailure: - return EACCES; - case kNtErrorGracefulDisconnect: - return EPIPE; - case kNtErrorHostDown: - return EHOSTUNREACH; - case kNtErrorHostUnreachable: - return EHOSTUNREACH; - case kNtErrorInsufficientBuffer: - return EFAULT; - case kNtErrorInvalidAddress: - return EADDRNOTAVAIL; - case kNtErrorInvalidFunction: - return EINVAL; - case kNtErrorInvalidNetname: - return EADDRNOTAVAIL; - case kNtErrorInvalidUserBuffer: - return EMSGSIZE; - case kNtErrorIoPending: - return EINPROGRESS; - case kNtErrorLockViolation: - return EACCES; - case kNtErrorMoreData: - return EMSGSIZE; - case kNtErrorNetnameDeleted: - return ECONNABORTED; - case kNtErrorNetworkAccessDenied: - return EACCES; - case kNtErrorNetworkBusy: - return ENETDOWN; - case kNtErrorNetworkUnreachable: - return ENETUNREACH; - case kNtErrorNoaccess: - return EFAULT; - case kNtErrorNonpagedSystemResources: - return ENOMEM; - case kNtErrorNotEnoughMemory: - return ENOMEM; - case kNtErrorNotEnoughQuota: - return ENOMEM; - case kNtErrorNotFound: - return ENOENT; - case kNtErrorNotLocked: - return EACCES; - case kNtErrorNotReady: - return EACCES; - case kNtErrorNotSupported: - return ENOTSUP; - case kNtErrorNoMoreFiles: - return ENOENT; - case kNtErrorNoSystemResources: - return ENOMEM; - case kNtErrorOperationAborted: - return EINTR; - case kNtErrorOutOfPaper: - return EACCES; - case kNtErrorPagedSystemResources: - return ENOMEM; - case kNtErrorPagefileQuota: - return ENOMEM; - case kNtErrorPathNotFound: - return ENOENT; - case kNtErrorPipeNotConnected: - return EPIPE; - case kNtErrorPortUnreachable: - return ECONNRESET; - case kNtErrorProtocolUnreachable: - return ENETUNREACH; - case kNtErrorRemNotList: - return ECONNREFUSED; - case kNtErrorRequestAborted: - return EINTR; - case kNtErrorReqNotAccep: - return EWOULDBLOCK; - case kNtErrorSectorNotFound: - return EACCES; - case kNtErrorSemTimeout: - return ETIMEDOUT; - case kNtErrorSharingViolation: - return EACCES; - case kNtErrorTooManyNames: - return ENOMEM; - case kNtErrorUnexpNetErr: - return ECONNABORTED; - case kNtErrorWorkingSetQuota: - return ENOMEM; - case kNtErrorWriteProtect: - return EACCES; - case kNtErrorWrongDisk: - return EACCES; - case WSAEACCES: - return EACCES; - case WSAEDISCON: - return EPIPE; - case WSAEFAULT: - return EFAULT; - case WSAEINPROGRESS: - return EBUSY; - case WSAEINVAL: - return EINVAL; - case WSAEPROCLIM: - return ENOMEM; - case WSAESHUTDOWN: - return EPIPE; - case WSANOTINITIALISED: - return ENETDOWN; - case WSASYSNOTREADY: - return ENETDOWN; - case WSAVERNOTSUPPORTED: - return ENOSYS; - default: - return error; - } -} diff --git a/libc/stdio/getdelim.c b/libc/stdio/getdelim.c index 9fc4a52a348..28001bc479f 100644 --- a/libc/stdio/getdelim.c +++ b/libc/stdio/getdelim.c @@ -54,7 +54,13 @@ ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) { for (i = 0;; i += m) { m = f->end - f->beg; if ((p = memchr(f->buf + f->beg, delim, m))) m = p + 1 - (f->buf + f->beg); - if (i + m + 1 > *n && !(*s = realloc(*s, (*n = i + m + 1)))) abort(); + if (i + m + 1 > *n) { + *n = i + m + 1; + *s = realloc(*s, *n); + if (!*s) { + abort(); + } + } memcpy(*s + i, f->buf + f->beg, m); (*s)[i + m] = '\0'; if ((f->beg += m) == f->end) f->beg = f->end = 0; diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 9ecd15cf9c1..9c11a73befe 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -54,7 +54,7 @@ wint_t fgetwc(FILE *) paramsnonnull(); wint_t ungetwc(wint_t, FILE *) paramsnonnull(); int getchar(void); int putchar(int); -int puts(const char *) paramsnonnull(); +int puts(const char *); ssize_t getline(char **, size_t *, FILE *) paramsnonnull(); ssize_t getdelim(char **, size_t *, int, FILE *) paramsnonnull(); FILE *fopen(const char *, const char *) paramsnonnull() nodiscard; diff --git a/libc/str/djbsort.c b/libc/str/djbsort.c index 66dfe7c684e..5b33859b6c1 100644 --- a/libc/str/djbsort.c +++ b/libc/str/djbsort.c @@ -59,7 +59,7 @@ void djbsort(int32_t *a, size_t n) { size_t m; if (IsAsan()) { if (__builtin_mul_overflow(n, 4, &m)) m = -1; - __asan_check(a, m); + __asan_verify(a, m); } if (n > 1) { if (X86_HAVE(AVX2)) { diff --git a/libc/str/longsort.c b/libc/str/longsort.c index bb3651be0aa..0155302f48c 100644 --- a/libc/str/longsort.c +++ b/libc/str/longsort.c @@ -66,7 +66,7 @@ void longsort(long *x, size_t n) { size_t t, m; if (IsAsan()) { if (__builtin_mul_overflow(n, sizeof(long), &m)) m = -1; - __asan_check(x, m); + __asan_verify(x, m); } if (n > 1) { t = 1ul << bsrl(n - 1); diff --git a/libc/str/memchr.c b/libc/str/memchr.c index ffcc8116d23..bef7c8c7cd6 100644 --- a/libc/str/memchr.c +++ b/libc/str/memchr.c @@ -70,7 +70,7 @@ noasan static inline const unsigned char *memchr_sse(const unsigned char *s, void *memchr(const void *s, int c, size_t n) { const void *r; if (X86_HAVE(SSE)) { - if (IsAsan()) __asan_check(s, n); + if (IsAsan()) __asan_verify(s, n); r = memchr_sse(s, c, n); } else { r = memchr_pure(s, c, n); diff --git a/libc/str/rawmemchr.c b/libc/str/rawmemchr.c index a70225cbc22..821f7aa54e3 100644 --- a/libc/str/rawmemchr.c +++ b/libc/str/rawmemchr.c @@ -62,7 +62,7 @@ noasan static inline const char *rawmemchr_sse(const char *s, unsigned char c) { void *rawmemchr(const void *s, int c) { const void *r; if (X86_HAVE(SSE)) { - if (IsAsan()) __asan_check(s, 1); + if (IsAsan()) __asan_verify(s, 1); r = rawmemchr_sse(s, c); } else { r = rawmemchr_pure(s, c); diff --git a/libc/str/strchr.c b/libc/str/strchr.c index 653514c0aad..32d96e6a966 100644 --- a/libc/str/strchr.c +++ b/libc/str/strchr.c @@ -65,7 +65,7 @@ noasan static inline const char *strchr_sse(const char *s, unsigned char c) { char *strchr(const char *s, int c) { const char *r; if (X86_HAVE(SSE)) { - if (IsAsan()) __asan_check(s, 1); + if (IsAsan()) __asan_verify(s, 1); r = strchr_sse(s, c); } else { r = strchr_pure(s, c); diff --git a/libc/str/strchrnul.c b/libc/str/strchrnul.c index afaa0556d74..981b9aaa6a2 100644 --- a/libc/str/strchrnul.c +++ b/libc/str/strchrnul.c @@ -61,7 +61,7 @@ noasan static inline const char *strchrnul_sse(const char *s, unsigned char c) { char *strchrnul(const char *s, int c) { const char *r; if (X86_HAVE(SSE)) { - if (IsAsan()) __asan_check(s, 1); + if (IsAsan()) __asan_verify(s, 1); r = strchrnul_sse(s, c); } else { r = strchrnul_pure(s, c); diff --git a/libc/str/timingsafe_bcmp.c b/libc/str/timingsafe_bcmp.c index 0891f3d9da1..797c3a5d619 100644 --- a/libc/str/timingsafe_bcmp.c +++ b/libc/str/timingsafe_bcmp.c @@ -137,8 +137,8 @@ int timingsafe_bcmp(const void *a, const void *b, size_t n) { return w | w >> 32; } else { if (IsAsan()) { - __asan_check(a, n); - __asan_check(b, n); + __asan_verify(a, n); + __asan_verify(b, n); } if (X86_HAVE(AVX)) { return timingsafe_bcmp_avx(p, q, n); diff --git a/libc/sysv/calls/__sys_munmap.s b/libc/sysv/calls/__sys_munmap.s new file mode 100644 index 00000000000..20f43dacd68 --- /dev/null +++ b/libc/sysv/calls/__sys_munmap.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall __sys_munmap,0x049049049204900b,globl,hidden diff --git a/libc/sysv/calls/sigaltstack.s b/libc/sysv/calls/sigaltstack.s deleted file mode 100644 index 2d0c4af52ef..00000000000 --- a/libc/sysv/calls/sigaltstack.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sigaltstack,0x1191200352035083,globl diff --git a/libc/sysv/calls/sys_munmap.s b/libc/sysv/calls/sys_munmap.s deleted file mode 100644 index 9139888cdc6..00000000000 --- a/libc/sysv/calls/sys_munmap.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sys_munmap,0x049049049204900b,globl,hidden diff --git a/libc/sysv/calls/sys_sigaltstack.s b/libc/sysv/calls/sys_sigaltstack.s new file mode 100644 index 00000000000..2965d83f13a --- /dev/null +++ b/libc/sysv/calls/sys_sigaltstack.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_sigaltstack,0x1191200352035083,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index c593852fe24..1855299a7b7 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -225,6 +225,7 @@ syscon mmap MAP_SHARED 1 1 1 1 1 1 # forced consensus & faked nt syscon mmap MAP_PRIVATE 2 2 2 2 2 2 # forced consensus & faked nt syscon mmap MAP_TYPE 15 15 15 15 15 15 # mask for type of mapping syscon mmap MAP_FIXED 0x10 0x10 0x10 0x10 0x10 0x10 # unix consensus; openbsd appears to forbid; faked nt +syscon mmap MAP_FIXED_NOREPLACE 0x8000000 0x8000000 0x8000000 0x8000000 0x8000000 0x8000000 # handled and defined by cosmo runtime; 0x100000 on linux 4.7+ syscon mmap MAP_ANONYMOUS 0x20 0x1000 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt syscon mmap MAP_GROWSDOWN 0x0100 0 0x0400 0x4000 0x4000 0x100000 # mandatory for OpenBSD stacks; MAP_STACK on Free/OpenBSD; MEM_TOP_DOWN on NT syscon mmap MAP_CONCEAL 0 0 0x20000 0x8000 0x8000 0 # omit from core dumps; MAP_NOCORE on FreeBSD @@ -506,7 +507,7 @@ syscon rlimit RLIMIT_RSS 5 5 5 5 5 127 # max physical memory size syscon rlimit RLIMIT_NPROC 6 7 7 7 7 127 # max number of processes; see fork()→EAGAIN; bsd consensus syscon rlimit RLIMIT_NOFILE 7 8 8 8 8 127 # max number of open files; see accept()→EMFILE/ENFILE; bsd consensus syscon rlimit RLIMIT_MEMLOCK 8 6 6 6 6 127 # max locked-in-memory address space; bsd consensus -syscon rlimit RLIMIT_AS 9 5 10 127 10 127 # max virtual memory size in bytes +syscon rlimit RLIMIT_AS 9 5 10 127 10 127 # max virtual memory size in bytes; this one actually works; we set this to RLIMIT_DATA on OpenBSD syscon rlimit RLIMIT_LOCKS 10 127 127 127 127 127 # max flock() / fcntl() locks; bsd consensus syscon rlimit RLIMIT_SIGPENDING 11 127 127 127 127 127 # max sigqueue() can enqueue; bsd consensus syscon rlimit RLIMIT_MSGQUEUE 12 127 127 127 127 127 # meh posix message queues; bsd consensus @@ -592,10 +593,12 @@ syscon sicode POLL_ERR 4 4 4 4 4 4 # SIGIO; i/o error; unix conse syscon sicode POLL_PRI 5 5 5 5 5 5 # SIGIO; high priority input available; unix consensus syscon sicode POLL_HUP 6 6 6 6 6 6 # SIGIO; device disconnected; unix consensus -# sigalstack() values +# sigaltstack() values # # group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary syscon ss SIGSTKSZ 0x2000 0x020000 0x8800 0x7000 0x7000 0x2000 +syscon ss SS_ONSTACK 1 1 1 1 1 1 # unix consensus +syscon ss SS_DISABLE 2 4 4 4 4 2 # bsd consensus # clock_{gettime,settime} timers # @@ -1898,9 +1901,6 @@ syscon misc MT_ST_CAN_PARTITIONS 0x0400 0 0 0 0 0 syscon misc MT_ST_HPLOADER_OFFSET 0x2710 0 0 0 0 0 syscon misc MT_ST_SCSI2LOGICAL 0x0800 0 0 0 0 0 -syscon misc SS_ONSTACK 1 1 1 1 1 0 # unix consensus -syscon misc SS_DISABLE 2 4 4 4 4 0 # bsd consensus - syscon misc SYNC_FILE_RANGE_WAIT_AFTER 4 0 0 0 0 0 syscon misc SYNC_FILE_RANGE_WAIT_BEFORE 1 0 0 0 0 0 syscon misc SYNC_FILE_RANGE_WRITE 2 0 0 0 0 0 diff --git a/libc/sysv/consts/SS_DISABLE.S b/libc/sysv/consts/SS_DISABLE.S index 83042334f23..ef27ee807f3 100644 --- a/libc/sysv/consts/SS_DISABLE.S +++ b/libc/sysv/consts/SS_DISABLE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,SS_DISABLE,2,4,4,4,4,0 +.syscon ss,SS_DISABLE,2,4,4,4,4,2 diff --git a/libc/sysv/consts/SS_ONSTACK.S b/libc/sysv/consts/SS_ONSTACK.S index 397c6091345..b00aa8c86f8 100644 --- a/libc/sysv/consts/SS_ONSTACK.S +++ b/libc/sysv/consts/SS_ONSTACK.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon misc,SS_ONSTACK,1,1,1,1,1,0 +.syscon ss,SS_ONSTACK,1,1,1,1,1,1 diff --git a/libc/sysv/consts/map.h b/libc/sysv/consts/map.h index 05d65680024..1378c4b2a5c 100644 --- a/libc/sysv/consts/map.h +++ b/libc/sysv/consts/map.h @@ -26,11 +26,12 @@ extern const long MAP_CONCEAL; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_TYPE 15 -#define MAP_FIXED 16 +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_TYPE 15 +#define MAP_FIXED 16 +#define MAP_FIXED_NOREPLACE 0x8000000 #define MAP_32BIT SYMBOLIC(MAP_32BIT) #define MAP_ANONYMOUS SYMBOLIC(MAP_ANONYMOUS) diff --git a/libc/sysv/consts/mremap.h b/libc/sysv/consts/mremap.h index 0a44090197d..cba963aba58 100644 --- a/libc/sysv/consts/mremap.h +++ b/libc/sysv/consts/mremap.h @@ -1,16 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_MREMAP_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_MREMAP_H_ -#include "libc/runtime/symbolic.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ -extern const long MREMAP_FIXED; -extern const long MREMAP_MAYMOVE; - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ - -#define MREMAP_MAYMOVE LITERALLY(1) -#define MREMAP_FIXED LITERALLY(2) +#define MREMAP_MAYMOVE 1 +#define MREMAP_FIXED 2 #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MREMAP_H_ */ diff --git a/libc/sysv/consts/sig.h b/libc/sysv/consts/sig.h index 115ba250dd9..4f9b3e746c4 100644 --- a/libc/sysv/consts/sig.h +++ b/libc/sysv/consts/sig.h @@ -27,7 +27,6 @@ extern const long SIGRTMAX; extern const long SIGRTMIN; extern const long SIGSEGV; extern const long SIGSTKFLT; -extern const long SIGSTKSZ; extern const long SIGSTOP; extern const long SIGSYS; extern const long SIGTERM; @@ -75,7 +74,6 @@ COSMOPOLITAN_C_END_ #define SIGRTMIN SYMBOLIC(SIGRTMIN) #define SIGSEGV LITERALLY(11) #define SIGSTKFLT SYMBOLIC(SIGSTKFLT) -#define SIGSTKSZ SYMBOLIC(SIGSTKSZ) #define SIGSTOP SYMBOLIC(SIGSTOP) #define SIGSYS SYMBOLIC(SIGSYS) #define SIGTERM LITERALLY(15) diff --git a/libc/sysv/consts/ss.h b/libc/sysv/consts/ss.h new file mode 100644 index 00000000000..1656da73959 --- /dev/null +++ b/libc/sysv/consts/ss.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_SS_H_ +#define COSMOPOLITAN_LIBC_SYSV_CONSTS_SS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +extern const long SS_DISABLE; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ + +#define SIGSTKSZ STACKSIZE +#define SS_ONSTACK 1 +#define SS_DISABLE SS_DISABLE + +#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_SS_H_ */ diff --git a/libc/sysv/restorert.S b/libc/sysv/restorert.S index 5aa5affc309..03f07377a0c 100644 --- a/libc/sysv/restorert.S +++ b/libc/sysv/restorert.S @@ -24,7 +24,7 @@ __restore_bt: nop .endfn __restore_bt,globl,hidden - nop # gap so that GetSymbol(st, addr - 1) fails + nop # gap so that __get_symbol(st, addr - 1) fails .align 16 __restore_rt: # @see gdb/amd64-linux-tdep.c mov $0x000f,%rax # [sic] diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 074b4ed0a3b..0b1b75675ac 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -47,7 +47,7 @@ scall sys_lseek 0x0c70c71de20c7008 globl hidden # netbsd+openbsd:evilpad scall __sys_mmap 0x0c50c51dd20c5009 globl hidden # netbsd+openbsd:pad scall sys_msync 0x115100041204101a globl hidden scall sys_mprotect 0x04a04a04a204a00a globl hidden -scall sys_munmap 0x049049049204900b globl hidden +scall __sys_munmap 0x049049049204900b globl hidden scall sys_sigaction 0x15402e1a0202e00d globl hidden # rt_sigaction on Lunix; it's complicated on NetBSD scall sys_sigprocmask 0x125030154203000e globl hidden # a.k.a. rt_sigprocmask, openbsd:byvalue scall sys_ioctl 0x0360360362036010 globl hidden @@ -164,7 +164,7 @@ scall getresuid 0xfff119168ffff076 globl # semantics aren't well-defined scall getresgid 0xfff11b169ffff078 globl # semantics aren't well-defined scall sigpending 0x124034034203407f globl # rt_sigpending on linux scall sys_sigsuspend 0x12606f155206f082 globl hidden # openbsd:byvalue -scall sigaltstack 0x1191200352035083 globl +scall sys_sigaltstack 0x1191200352035083 globl hidden scall sys_mknod 0x1c200e00e200e085 globl hidden scall mknodat 0x1cc14022fffff103 globl # FreeBSD 12+ scall sys_mkfifo 0x0840840842084fff globl hidden diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 5f757ce6647..f1ddd4f68b4 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -21,6 +21,7 @@ #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/map.h" #include "libc/macros.internal.h" +#include "libc/sysv/consts/prot.h" #include "libc/nexgen32e/macros.h" /* ▄▄▄ @@ -349,6 +350,34 @@ _init_systemfive_stack: # determinism ftw! syscall 2: test %rax,%rax js 1b + +// prevent operating system from auto-mapping stack +// we guarantee stack overflows are always detected +// so long as you never use -DSTACK_FRAME_UNLIMITED +// TODO: Why does this fail sometimes with FreeBSD? + testb IsFreebsd() + jnz 9f + push %rax + push %rdx + push %r11 + mov __NR_mprotect,%eax + mov $PAGESIZE,%esi + xor %edx,%edx # PROT_NONE + syscall + pop %r11 + pop %rdx + pop %rax +9: + +// update the memory intervals +// m.i 0 4 +// m.n 8 4 +// m.p 16 8 +// m.p[0].x 24 4 +// m.p[0].y 28 4 +// m.p[0].h 32 8 +// m.p[0].prot 40 4 +// m.p[0].flags 44 4 .weak _mmi ezlea _mmi,cx test %rcx,%rcx @@ -360,12 +389,14 @@ _init_systemfive_stack: # determinism ftw! movb $1,(%rcx) # _mmi.i mov %r11d,24(%rcx) # _mmi.s[0].x mov %r9d,28(%rcx) # _mmi.s[0].y - mov %edx,36(%rcx) # _mmi.s[0].prot - mov %r10d,40(%rcx) # _mmi.s[0].flags + orq $-1,32(%rcx) # _mmi.s[0].h + mov %edx,40(%rcx) # _mmi.s[0].prot + mov %r10d,44(%rcx) # _mmi.s[0].flags 3: pop %r9 # restore stack size pop %rsi pop %rdi leave +// switch stacks pop %rcx lea (%rax,%r9),%rsp sub $ape_stack_align,%rsp # openbsd:stackbound diff --git a/libc/testlib/leaks.c b/libc/testlib/leaks.c new file mode 100644 index 00000000000..857e689f747 --- /dev/null +++ b/libc/testlib/leaks.c @@ -0,0 +1,106 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/intrin/asan.internal.h" +#include "libc/log/libfatal.internal.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/testlib/testlib.h" + +static bool once; +static bool hasleaks; + +static noasan void CheckLeak(void *x, void *y, size_t n, void *a) { + if (n) { + if (IsAsan()) { + if (__asan_get_heap_size(x)) { + hasleaks = true; + } + } else { + hasleaks = true; + } + } +} + +static noasan void OnMemory(void *x, void *y, size_t n, void *a) { + static int i; + if (n) { + if (++i < 20) { + __printf("%p %,d bytes", x, n); + if (IsAsan()) { + __asan_print_trace(x); + } + __printf("\n"); + } + if (i == 20) { + __printf("etc. etc.\n"); + } + } +} + +static noasan bool HasLeaks(void) { + malloc_inspect_all(CheckLeak, 0); + return hasleaks; +} + +/** + * Tests for memory leaks. + * + * This function needs to call __cxa_finalize(). Therefore any runtime + * services that depend on malloc() cannot be used, after calling this + * function. + */ +noasan void testlib_checkformemoryleaks(void) { + struct mallinfo mi; + if (!cmpxchg(&once, false, true)) { + __printf("testlib_checkformemoryleaks() may only be called once\n"); + _Exit(1); + } + __cxa_finalize(0); + if (!IsAsan()) { + /* TODO(jart): How can we make this work without ASAN? */ + return; + } + malloc_trim(0); + if (HasLeaks()) { + mi = mallinfo(); + __printf("\n" + "UNFREED MEMORY\n" + "%s\n" + "max allocated space %,*d\n" + "total allocated space %,*d\n" + "total free space %,*d\n" + "releasable space %,*d\n" + "mmaped space %,*d\n" + "non-mmapped space %,*d\n" + "\n", + __argv[0], 16l, mi.usmblks, 16l, mi.uordblks, 16l, mi.fordblks, + 16l, mi.hblkhd, 16l, mi.keepcost, 16l, mi.arena); + if (!IsAsan()) { + __printf("# NOTE: Use `make -j8 MODE=dbg` for malloc() backtraces\n"); + } + malloc_inspect_all(OnMemory, 0); + __printf("\n"); + PrintMemoryIntervals(2, &_mmi); + /* PrintSystemMappings(2); */ + /* PrintGarbage(); */ + _Exit(78); + } +} diff --git a/libc/testlib/quota.c b/libc/testlib/quota.c index d2ebbf8821c..810528616c7 100644 --- a/libc/testlib/quota.c +++ b/libc/testlib/quota.c @@ -40,77 +40,56 @@ static noasan relegated uint64_t CountMappedBytes(void) { return x; } -static relegated void DieBecauseOfQuota(char *p, int rc, const char *message) { - int e; +static relegated void DieBecauseOfQuota(int rc, const char *message) { + int e = errno; char hostname[32]; - e = errno; - __restore_tty(2); __stpcpy(hostname, "unknown"); gethostname(hostname, sizeof(hostname)); - p = __stpcpy(p, message); - p = __stpcpy(p, " on "); - p = __stpcpy(p, hostname); - p = __stpcpy(p, " pid "); - p = __intcpy(p, __getpid()); - p = __stpcpy(p, "\n"); - __write(__fatalbuf, p - __fatalbuf); + __printf("%s on %s pid %d\n", message, hostname, (long)__getpid()); PrintBacktraceUsingSymbols(2, 0, GetSymbolTable()); - exit(rc); + _Exit(rc); } static relegated void OnXcpu(int sig) { - DieBecauseOfQuota(__fatalbuf, 23, "\n\nSIGXCPU: ran out of cpu"); + __restore_tty(2); + DieBecauseOfQuota(23, "\n\nSIGXCPU: ran out of cpu"); } static relegated void OnXfsz(int sig) { - DieBecauseOfQuota(__fatalbuf, 25, "\n\nSIGXFSZ: exceeded maximum file size"); + __restore_tty(2); + DieBecauseOfQuota(25, "\n\nSIGXFSZ: exceeded maximum file size"); } relegated void __oom_hook(size_t request) { int e; - char *p; uint64_t toto, newlim; struct MallocStats stats; + __restore_tty(2); e = errno; - p = __fatalbuf; toto = CountMappedBytes(); stats = dlmalloc_stats(g_dlmalloc); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "WE REQUIRE MORE VESPENE GAS"); - if (e != ENOMEM) { - p = __stpcpy(p, " ("); - p = __stpcpy(p, strerror(e)); - p = __stpcpy(p, ")"); - } - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "mmap last request = "); - p = __intcpy(p, request); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "mmapped system bytes = "); - p = __intcpy(p, toto); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "malloc max system bytes = "); - p = __intcpy(p, stats.maxfp); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "malloc system bytes = "); - p = __intcpy(p, stats.fp); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "malloc in use bytes = "); - p = __intcpy(p, stats.used); - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "\n"); + __printf("\n\nWE REQUIRE MORE VESPENE GAS"); + if (e != ENOMEM) __printf(" (%s)", strerror(e)); + __printf("\n" + "mmap last request = %d\n" + "mmapped system bytes = %d\n" + "malloc max system bytes = %d\n" + "malloc system bytes = %d\n" + "malloc in use bytes = %d\n" + "\n", + request, toto, stats.maxfp, stats.fp, stats.used); if (IsRunningUnderMake()) { newlim = toto + request; newlim += newlim >> 1; newlim = roundup2pow(newlim); - p = __stpcpy(p, "FIX CODE OR TUNE QUOTA += -M"); - p = __intcpy(p, newlim / (1024 * 1024)); - p = __stpcpy(p, "m\n"); + __printf("FIX CODE OR TUNE QUOTA += -M%dm\n", newlim / (1024 * 1024)); } - p = __stpcpy(p, "\n"); - p = __stpcpy(p, "THE STRAW THAT BROKE THE CAMEL'S BACK\n"); - DieBecauseOfQuota(p, 42, "MAP_FAILED: exceeded memory quota"); + __printf("\n"); + PrintMemoryIntervals(2, &_mmi); + __printf("\nTHE STRAW THAT BROKE THE CAMEL'S BACK\n"); + PrintBacktraceUsingSymbols(2, 0, GetSymbolTable()); + PrintSystemMappings(2); + _Exit(42); } static textstartup void InstallQuotaHandlers(void) { diff --git a/libc/testlib/showerror.c b/libc/testlib/showerror.c index 3b4abb682ff..657f5c5d610 100644 --- a/libc/testlib/showerror.c +++ b/libc/testlib/showerror.c @@ -19,6 +19,7 @@ #include "libc/bits/safemacros.internal.h" #include "libc/fmt/fmt.h" #include "libc/log/color.internal.h" +#include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" #include "libc/testlib/testlib.h" @@ -36,38 +37,18 @@ testonly void testlib_showerror(const char *file, int line, const char *func, /* TODO(jart): Pay off tech debt re duplication */ __getpid(); /* make strace easier to read */ __getpid(); - p = __fatalbuf; - p = __stpcpy(p, RED2); - p = __stpcpy(p, "error"); - p = __stpcpy(p, UNBOLD); - p = __stpcpy(p, BLUE1); - p = __stpcpy(p, ":"); - p = __stpcpy(p, file); - p = __stpcpy(p, ":"); - p = __intcpy(p, line); - p = __stpcpy(p, RESET); - p = __stpcpy(p, ": "); - p = __stpcpy(p, method); - p = __stpcpy(p, "() in "); - p = __stpcpy(p, func); - p = __stpcpy(p, "("); - p = __stpcpy(p, g_fixturename); - p = __stpcpy(p, ")\n\t"); - p = __stpcpy(p, code); - p = __stpcpy(p, "\n\t\tneed "); - p = __stpcpy(p, v1); - p = __stpcpy(p, " "); - p = __stpcpy(p, symbol); - p = __stpcpy(p, "\n\t\t got "); - p = __stpcpy(p, v2); - p = __stpcpy(p, "\n\t"); - p = __stpcpy(p, SUBTLE); - p = __stpcpy(p, strerror(errno)); - p = __stpcpy(p, "\n\t"); - p = __stpcpy(p, program_invocation_name); - p = __stpcpy(p, RESET); - p = __stpcpy(p, "\n"); - __write(__fatalbuf, p - __fatalbuf); + __printf("%serror%s:%s:%d%s: %s() in %s(%s)\n" + "\t%s\n" + "\t\tneed %s %s\n" + "\t\t got %s\n" + "\t%s%s\n" + "\t%s%s\n", + !g_isterminalinarticulate ? "\e[91;1m" : "", + !g_isterminalinarticulate ? "\e[22;94;49m" : "", file, (long)line, + !g_isterminalinarticulate ? "\e[0m" : "", method, func, + g_fixturename, code, v1, symbol, v2, + !g_isterminalinarticulate ? "\e[35m" : "", strerror(errno), + program_executable_name, !g_isterminalinarticulate ? "\e[0m" : ""); free_s(&v1); free_s(&v2); } diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index a1e2668562c..6e3fb3263e1 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -352,6 +352,7 @@ void thrashcodecache(void); void testlib_finish(void); void testlib_runalltests(void); void testlib_runallbenchmarks(void); +void testlib_checkformemoryleaks(void); void testlib_runtestcases(testfn_t *, testfn_t *, testfn_t); void testlib_runcombos(testfn_t *, testfn_t *, const struct TestFixture *, const struct TestFixture *); diff --git a/libc/testlib/testlib.mk b/libc/testlib/testlib.mk index 06b20541163..fc2274f506b 100644 --- a/libc/testlib/testlib.mk +++ b/libc/testlib/testlib.mk @@ -59,6 +59,7 @@ LIBC_TESTLIB_A_SRCS_C = \ libc/testlib/comborunner.c \ libc/testlib/contains.c \ libc/testlib/endswith.c \ + libc/testlib/leaks.c \ libc/testlib/yield.c \ libc/testlib/ezbenchcontrol.c \ libc/testlib/ezbenchreport.c \ @@ -189,6 +190,7 @@ LIBC_TESTMAIN_DIRECTDEPS = \ LIBC_SYSV_CALLS \ LIBC_TESTLIB \ LIBC_TESTLIB_RUNNER \ + THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GETOPT LIBC_TESTMAIN_DEPS := \ diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index ef9e35e34c0..803190f9a73 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -21,8 +21,15 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" +#include "libc/mem/mem.h" #include "libc/nexgen32e/x86feature.h" +#include "libc/runtime/internal.h" +#include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/ex.h" @@ -42,6 +49,7 @@ Flags:\n\ \n" STATIC_YOINK("__die"); +STATIC_YOINK("__get_symbol_by_addr"); STATIC_YOINK("testlib_quota_handlers"); static bool runbenchmarks_; @@ -75,7 +83,7 @@ void GetOpts(int argc, char *argv[]) { /** * Generic test program main function. */ -int main(int argc, char *argv[]) { +noasan int main(int argc, char *argv[]) { const char *comdbg; __log_level = kLogInfo; GetOpts(argc, argv); @@ -86,9 +94,14 @@ int main(int argc, char *argv[]) { testlib_runalltests(); if (!g_testlib_failed && runbenchmarks_ && weaken(testlib_runallbenchmarks)) { weaken(testlib_runallbenchmarks)(); + if (!g_testlib_failed) { + testlib_checkformemoryleaks(); + } if (!g_testlib_failed && IsRunningUnderMake()) { return 254; /* compile.com considers this 0 and propagates output */ } + } else if (!g_testlib_failed) { + testlib_checkformemoryleaks(); } - return min(255, g_testlib_failed); + _Exit(min(255, g_testlib_failed)); } diff --git a/libc/thread/attr.c b/libc/thread/attr.c index 506ee800a0c..cc139fb30db 100644 --- a/libc/thread/attr.c +++ b/libc/thread/attr.c @@ -16,16 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/attr.h" #include "libc/errno.h" +#include "libc/thread/attr.h" -#define MIN_STACKSIZE (8*PAGESIZE) +#define MIN_STACKSIZE (8 * PAGESIZE) #define MIN_GUARDSIZE PAGESIZE // CTOR/DTOR int cthread_attr_init(cthread_attr_t* attr) { - attr->stacksize = 1024*PAGESIZE; // 4 MiB - attr->guardsize = 16*PAGESIZE; // 64 KiB + attr->stacksize = 1024 * PAGESIZE; // 4 MiB + attr->guardsize = 16 * PAGESIZE; // 64 KiB attr->mode = CTHREAD_CREATE_JOINABLE; return 0; } @@ -36,7 +36,7 @@ int cthread_attr_destroy(cthread_attr_t* attr) { // stacksize int cthread_attr_setstacksize(cthread_attr_t* attr, size_t size) { - if (size & (PAGESIZE-1)) return EINVAL; + if (size & (PAGESIZE - 1)) return EINVAL; if (size < MIN_STACKSIZE) return EINVAL; attr->stacksize = size; return 0; @@ -47,7 +47,7 @@ size_t cthread_attr_getstacksize(const cthread_attr_t* attr) { // guardsize int cthread_attr_setguardsize(cthread_attr_t* attr, size_t size) { - if (size & (PAGESIZE-1)) return EINVAL; + if (size & (PAGESIZE - 1)) return EINVAL; if (size < MIN_GUARDSIZE) return EINVAL; attr->guardsize = size; return 0; @@ -58,7 +58,8 @@ size_t cthread_attr_getguardsize(const cthread_attr_t* attr) { // detachstate int cthread_attr_setdetachstate(cthread_attr_t* attr, int mode) { - if (mode & ~(CTHREAD_CREATE_JOINABLE | CTHREAD_CREATE_DETACHED)) return EINVAL; + if (mode & ~(CTHREAD_CREATE_JOINABLE | CTHREAD_CREATE_DETACHED)) + return EINVAL; attr->mode = mode; return 0; } diff --git a/libc/thread/attr.h b/libc/thread/attr.h index cc682d530c5..e802ea78f13 100644 --- a/libc/thread/attr.h +++ b/libc/thread/attr.h @@ -1,5 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_THREAD_ATTR_H_ #define COSMOPOLITAN_LIBC_THREAD_ATTR_H_ + +#define CTHREAD_CREATE_DETACHED 1 +#define CTHREAD_CREATE_JOINABLE 0 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -7,27 +11,17 @@ COSMOPOLITAN_C_START_ * @fileoverview cosmopolitan thread attributes */ -#define CTHREAD_CREATE_DETACHED 1 -#define CTHREAD_CREATE_JOINABLE 0 - typedef struct cthread_attr_t { - size_t stacksize, guardsize; - int mode; + size_t stacksize, guardsize; + int mode; } cthread_attr_t; -// CTOR/DTOR int cthread_attr_init(cthread_attr_t*); int cthread_attr_destroy(cthread_attr_t*); - -// stacksize int cthread_attr_setstacksize(cthread_attr_t*, size_t); size_t thread_attr_getstacksize(const cthread_attr_t*); - -// guardsize int cthread_attr_setguardsize(cthread_attr_t*, size_t); size_t cthread_attr_getguardsize(const cthread_attr_t*); - -// detachstate int cthread_attr_setdetachstate(cthread_attr_t*, int); int cthread_attr_getdetachstate(const cthread_attr_t*); diff --git a/libc/thread/create.c b/libc/thread/create.c index 2f1c570931a..c5b5d0c4200 100644 --- a/libc/thread/create.c +++ b/libc/thread/create.c @@ -16,41 +16,44 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/create.h" +#include "libc/errno.h" #include "libc/linux/clone.h" #include "libc/runtime/runtime.h" -#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/prot.h" -#include "libc/errno.h" - +#include "libc/thread/create.h" static cthread_t _thread_allocate(const cthread_attr_t* attr) { size_t stacksize = attr->stacksize; size_t guardsize = attr->guardsize; // FIXME: properly count TLS size size_t tlssize = 0; - - size_t totalsize = 3*guardsize + stacksize + tlssize + sizeof(struct cthread_descriptor_t); - totalsize = (totalsize + PAGESIZE-1) & -PAGESIZE; - - uintptr_t mem = (uintptr_t)mmap(NULL, totalsize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + + size_t totalsize = + 3 * guardsize + stacksize + tlssize + sizeof(struct cthread_descriptor_t); + totalsize = (totalsize + PAGESIZE - 1) & -PAGESIZE; + + uintptr_t mem = (uintptr_t)mmap(NULL, totalsize, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == -1) return NULL; - - void* alloc_bottom = (void*) mem; + + void* alloc_bottom = (void*)mem; void* stack_bottom = (void*)(mem + guardsize); void* stack_top = (void*)(mem + guardsize + stacksize); void* tls_bottom = (void*)(mem + guardsize + stacksize + guardsize); void* tls_top = (void*)(mem + totalsize - guardsize); void* alloc_top = (void*)(mem + totalsize); - - if (mprotect(stack_bottom, (uintptr_t)stack_top - (uintptr_t)stack_bottom, PROT_READ | PROT_WRITE) != 0 || - mprotect(tls_bottom, (uintptr_t)tls_top - (uintptr_t)tls_bottom, PROT_READ | PROT_WRITE) != 0) { + + if (mprotect(stack_bottom, (uintptr_t)stack_top - (uintptr_t)stack_bottom, + PROT_READ | PROT_WRITE) != 0 || + mprotect(tls_bottom, (uintptr_t)tls_top - (uintptr_t)tls_bottom, + PROT_READ | PROT_WRITE) != 0) { munmap(alloc_bottom, totalsize); return NULL; } - + cthread_t td = (cthread_t)tls_top - 1; td->self = td; td->stack.top = stack_top; @@ -59,45 +62,48 @@ static cthread_t _thread_allocate(const cthread_attr_t* attr) { td->tls.bottom = tls_bottom; td->alloc.top = alloc_top; td->alloc.bottom = alloc_bottom; - td->state = (attr->mode & CTHREAD_CREATE_DETACHED) ? cthread_detached : cthread_started; - + td->state = (attr->mode & CTHREAD_CREATE_DETACHED) ? cthread_detached + : cthread_started; + return td; } -int cthread_create(cthread_t*restrict p, const cthread_attr_t*restrict attr, int (*func)(void*), void*restrict arg) { - extern wontreturn void _thread_run(int(*func)(void*), void* arg); - +int cthread_create(cthread_t* restrict p, const cthread_attr_t* restrict attr, + int (*func)(void*), void* restrict arg) { + extern wontreturn void _thread_run(int (*func)(void*), void* arg); + cthread_attr_t default_attr; cthread_attr_init(&default_attr); cthread_t td = _thread_allocate(attr ? attr : &default_attr); cthread_attr_destroy(&default_attr); if (!td) return errno; - + *p = td; - + register cthread_t td_ asm("r8") = td; register int* ptid_ asm("rdx") = &td->tid; register int* ctid_ asm("r10") = &td->tid; - register int(*func_)(void*) asm("r12") = func; + register int (*func_)(void*) asm("r12") = func; register void* arg_ asm("r13") = arg; - - long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | CLONE_THREAD | /*CLONE_IO |*/ CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; + + long flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | + CLONE_PARENT | CLONE_THREAD | /*CLONE_IO |*/ CLONE_SETTLS | + CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; int rc; // asm ensures the (empty) stack of the child thread is not used - asm volatile( - "syscall\n\t" // clone - "test\t%0, %0\n\t" // if not child - "jne\t.L.cthread_create.%=\n\t" // jump to `parent` label - "xor\t%%rbp, %%rbp\n\t" // reset stack frame pointer - "mov\t%2, %%rdi\n\t" - "call\t*%1\n\t" // call `func(arg)` - "mov\t%%rax, %%rdi\n\t" - "jmp\tcthread_exit\n" // exit thread - ".L.cthread_create.%=:" - : "=a"(rc) - : "r"(func_), "r"(arg_), "0"(__NR_clone), "D"(flags), "S"(td->stack.top), "r"(ptid_), "r"(ctid_), "r"(td_) - : "rcx", "r11", "cc", "memory" - ); + asm volatile("syscall\n\t" // clone + "test\t%0, %0\n\t" // if not child + "jne\t.L.cthread_create.%=\n\t" // jump to `parent` label + "xor\t%%rbp, %%rbp\n\t" // reset stack frame pointer + "mov\t%2, %%rdi\n\t" + "call\t*%1\n\t" // call `func(arg)` + "mov\t%%rax, %%rdi\n\t" + "jmp\tcthread_exit\n" // exit thread + ".L.cthread_create.%=:" + : "=a"(rc) + : "r"(func_), "r"(arg_), "0"(__NR_clone), "D"(flags), + "S"(td->stack.top), "r"(ptid_), "r"(ctid_), "r"(td_) + : "rcx", "r11", "cc", "memory"); if (__builtin_expect(rc < 0, 0)) { // `clone` has failed. The thread must be deallocated. size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom); diff --git a/libc/thread/create.h b/libc/thread/create.h index b6e77b95093..b13fd614c41 100644 --- a/libc/thread/create.h +++ b/libc/thread/create.h @@ -9,9 +9,9 @@ COSMOPOLITAN_C_START_ * @fileoverview Create a cosmopolitan thread */ -int cthread_create(cthread_t*restrict, const cthread_attr_t*restrict, int (*)(void*), void*restrict); - +int cthread_create(cthread_t* restrict, const cthread_attr_t* restrict, + int (*)(void*), void* restrict); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_THREAD_CREATE_H_ */ +#endif /* COSMOPOLITAN_LIBC_THREAD_CREATE_H_ */ diff --git a/libc/thread/descriptor.h b/libc/thread/descriptor.h index 793590ffd0c..587092d12d7 100644 --- a/libc/thread/descriptor.h +++ b/libc/thread/descriptor.h @@ -14,9 +14,8 @@ enum cthread_state { cthread_detached = 4, }; - struct cthread_descriptor_t { - struct cthread_descriptor_t* self; // mandatory for TLS + struct cthread_descriptor_t* self; /* mandatory for TLS */ struct { void *top, *bottom; } stack, tls, alloc; diff --git a/libc/thread/detach.c b/libc/thread/detach.c index a6defc26289..e6e0398f219 100644 --- a/libc/thread/detach.c +++ b/libc/thread/detach.c @@ -16,13 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/detach.h" -#include "libc/thread/descriptor.h" #include "libc/runtime/runtime.h" +#include "libc/thread/descriptor.h" +#include "libc/thread/detach.h" int cthread_detach(cthread_t td) { int state; - asm volatile("lock xadd\t%1, %0" : "+m"(td->state), "=r"(state) : "1"(cthread_detached) : "cc"); + asm volatile("lock xadd\t%1, %0" + : "+m"(td->state), "=r"(state) + : "1"(cthread_detached) + : "cc"); if ((state & cthread_finished)) { size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom); munmap(td->alloc.bottom, size); diff --git a/libc/thread/exit.c b/libc/thread/exit.c index f1226074424..c2cf0c578da 100644 --- a/libc/thread/exit.c +++ b/libc/thread/exit.c @@ -16,29 +16,28 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/sysv/consts/nr.h" +#include "libc/thread/descriptor.h" #include "libc/thread/exit.h" #include "libc/thread/self.h" -#include "libc/thread/descriptor.h" -#include "libc/sysv/consts/nr.h" wontreturn void cthread_exit(int rc) { cthread_t td = cthread_self(); td->rc = rc; size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom); - + int state; - asm volatile( - "lock xadd\t%1, %0\n\t" // mark thread as finished - "test\t%2, %b1\n\t" // test if thread was detached - "jz .L.cthread_exit.%=\n\t" // skip unmap if not detached - "syscall\n" // unmap thread - ".L.cthread_exit.%=:\n\t" - "mov\t%%rbx, %%rdi\n\t" //rc - "mov\t$60, %%rax\n\t" - "syscall" // thread exit - : "+m"(td->state), "=&r"(state) - : "I"(cthread_detached), "1"(cthread_finished), "a"(__NR_munmap), "b"(rc), "D"(td->alloc.bottom), "S"(size) - : "rcx", "r11", "cc", "memory" - ); + asm volatile("lock xadd\t%1, %0\n\t" // mark thread as finished + "test\t%2, %b1\n\t" // test if thread was detached + "jz .L.cthread_exit.%=\n\t" // skip unmap if not detached + "syscall\n" // unmap thread + ".L.cthread_exit.%=:\n\t" + "mov\t%%rbx, %%rdi\n\t" // rc + "mov\t$60, %%rax\n\t" + "syscall" // thread exit + : "+m"(td->state), "=&r"(state) + : "I"(cthread_detached), "1"(cthread_finished), "a"(__NR_munmap), + "b"(rc), "D"(td->alloc.bottom), "S"(size) + : "rcx", "r11", "cc", "memory"); unreachable; } diff --git a/libc/thread/join.c b/libc/thread/join.c index bf21ee9a913..e816a02046d 100644 --- a/libc/thread/join.c +++ b/libc/thread/join.c @@ -16,33 +16,35 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/join.h" -#include "libc/thread/descriptor.h" #include "libc/runtime/runtime.h" -#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/futex.h" +#include "libc/sysv/consts/nr.h" +#include "libc/thread/descriptor.h" +#include "libc/thread/join.h" int cthread_join(cthread_t td, int* rc) { - int tid = td->tid; // tid must be loaded before lock xadd + int tid = td->tid; // tid must be loaded before lock xadd // otherwise, tid could be set to 0 even though `state` is not finished - + // mark thread as joining int state; - asm volatile("lock xadd\t%1, %0" : "+m"(td->state), "=r"(state) : "1"(cthread_joining) : "cc"); - + asm volatile("lock xadd\t%1, %0" + : "+m"(td->state), "=r"(state) + : "1"(cthread_joining) + : "cc"); + if (!(state & cthread_finished)) { - int flags = FUTEX_WAIT; // PRIVATE makes it hang + int flags = FUTEX_WAIT; // PRIVATE makes it hang register struct timespec* timeout asm("r10") = NULL; - asm volatile ( - "syscall" - : - : "a"(__NR_futex), "D"(&td->tid), "S"(flags), "d"(tid), "r"(timeout) - : "rcx", "r11", "cc", "memory" - ); + asm volatile("syscall" + : /* no outputs */ + : "a"(__NR_futex), "D"(&td->tid), "S"(flags), "d"(tid), + "r"(timeout) + : "rcx", "r11", "cc", "memory"); } - + *rc = td->rc; - + size_t size = (intptr_t)(td->alloc.top) - (intptr_t)(td->alloc.bottom); munmap(td->alloc.bottom, size); return 0; diff --git a/libc/thread/nativesem.c b/libc/thread/nativesem.c index 3930dd669d3..2e7eccafbe5 100644 --- a/libc/thread/nativesem.c +++ b/libc/thread/nativesem.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/atomic.h" +#include "libc/sysv/consts/futex.h" +#include "libc/sysv/consts/nr.h" #include "libc/thread/nativesem.h" #include "libc/thread/yield.h" -#include "libc/sysv/consts/nr.h" -#include "libc/sysv/consts/futex.h" -#include "libc/bits/atomic.h" #define CTHREAD_THREAD_VAL_BITS 32 @@ -35,78 +35,98 @@ int cthread_native_sem_destroy(cthread_native_sem_t* sem) { int cthread_native_sem_signal(cthread_native_sem_t* sem) { uint64_t count; - asm volatile("lock xadd\t%1, %0" : "+m"(sem->linux.count), "=r"(count) : "1"(1) : "cc"); - + asm volatile("lock xadd\t%1, %0" + : "+m"(sem->linux.count), "=r"(count) + : "1"(1) + : "cc"); + if ((count >> CTHREAD_THREAD_VAL_BITS)) { int flags = FUTEX_WAKE; - + // WARNING: an offset of 4 bytes would be required on little-endian archs void* wait_address = &sem->linux.count; - asm volatile ( - "syscall" - : - : "a"(__NR_futex), "D"(wait_address), "S"(flags), "d"(1) - : "rcx", "r11", "cc", "memory" - ); + asm volatile("syscall" + : /* no outputs */ + : "a"(__NR_futex), "D"(wait_address), "S"(flags), "d"(1) + : "rcx", "r11", "cc", "memory"); } - + return 0; } -int cthread_native_sem_wait_slow(cthread_native_sem_t* sem, const struct timespec* timeout) { +int cthread_native_sem_wait_slow(cthread_native_sem_t* sem, + const struct timespec* timeout) { uint64_t count; - + // record current thread as waiter - asm volatile("lock xadd\t%1, %0" : "+m"(sem->linux.count), "=r"(count) : "1"((uint64_t)1 << CTHREAD_THREAD_VAL_BITS) : "cc"); - + asm volatile("lock xadd\t%1, %0" + : "+m"(sem->linux.count), "=r"(count) + : "1"((uint64_t)1 << CTHREAD_THREAD_VAL_BITS) + : "cc"); + for (;;) { // try to acquire the semaphore, as well as remove itself from waiters - if ((uint32_t)count > 0 && atomic_compare_exchange_weak(&sem->linux.count, count, count - 1 - ((uint64_t)1 << CTHREAD_THREAD_VAL_BITS))) break; - + if ((uint32_t)count > 0 && + atomic_compare_exchange_weak( + &sem->linux.count, count, + count - 1 - ((uint64_t)1 << CTHREAD_THREAD_VAL_BITS))) + break; + int flags = FUTEX_WAIT; register struct timespec* timeout_ asm("r10") = timeout; - + // WARNING: an offset of 4 bytes would be required on little-endian archs void* wait_address = &sem->linux.count; - asm volatile ( - "syscall" - : - : "a"(__NR_futex), "D"(wait_address), "S"(flags), "d"(count), "r"(timeout_) - : "rcx", "r11", "cc", "memory" - ); + asm volatile("syscall" + : /* no outputs */ + : "a"(__NR_futex), "D"(wait_address), "S"(flags), "d"(count), + "r"(timeout_) + : "rcx", "r11", "cc", "memory"); count = atomic_load(&sem->linux.count); } - + return 0; } -int cthread_native_sem_wait_spin_yield(cthread_native_sem_t* sem, uint64_t count, int yield, const struct timespec* timeout) { +int cthread_native_sem_wait_spin_yield(cthread_native_sem_t* sem, + uint64_t count, int yield, + const struct timespec* timeout) { // spin on yield while (yield-- > 0) { - if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) break; // a thread is already waiting in queue - if ((uint32_t)count > 0 && atomic_compare_exchange_weak(&sem->linux.count, count, count-1)) return 0; + if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) + break; // a thread is already waiting in queue + if ((uint32_t)count > 0 && + atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) + return 0; cthread_yield(); } - + return cthread_native_sem_wait_slow(sem, timeout); } -int cthread_native_sem_wait_spin(cthread_native_sem_t* sem, uint64_t count, int spin, int yield, const struct timespec* timeout) { +int cthread_native_sem_wait_spin(cthread_native_sem_t* sem, uint64_t count, + int spin, int yield, + const struct timespec* timeout) { // spin on pause while (spin-- > 0) { if ((count >> CTHREAD_THREAD_VAL_BITS) != 0) break; - if ((uint32_t)count > 0 && atomic_compare_exchange_weak(&sem->linux.count, count, count-1)) return 0; - asm volatile ("pause"); + if ((uint32_t)count > 0 && + atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) + return 0; + asm volatile("pause"); } - + return cthread_native_sem_wait_spin_yield(sem, count, yield, timeout); } -int cthread_native_sem_wait(cthread_native_sem_t* sem, int spin, int yield, const struct timespec* timeout) { +int cthread_native_sem_wait(cthread_native_sem_t* sem, int spin, int yield, + const struct timespec* timeout) { uint64_t count = atomic_load(&sem->linux.count); - + // uncontended - if ((count >> 32) == 0 && (uint32_t)count > 0 && atomic_compare_exchange_weak(&sem->linux.count, count, count-1)) return 0; - + if ((count >> 32) == 0 && (uint32_t)count > 0 && + atomic_compare_exchange_weak(&sem->linux.count, count, count - 1)) + return 0; + return cthread_native_sem_wait_spin(sem, count, spin, yield, timeout); } diff --git a/libc/thread/self.h b/libc/thread/self.h index 4ab15498c1c..2885eba8936 100644 --- a/libc/thread/self.h +++ b/libc/thread/self.h @@ -8,13 +8,16 @@ COSMOPOLITAN_C_START_ * @fileoverview get the thread descriptor of the current thread */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) inline cthread_t cthread_self(void) { cthread_t self; - asm ("mov %%fs:0, %0" : "=r"(self)); + asm("mov\t%%fs:0,%0" : "=r"(self)); return self; } - +#else +cthread_t cthread_self(void); +#endif COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_THREAD_SELF_H_ */ +#endif /* COSMOPOLITAN_LIBC_THREAD_SELF_H_ */ diff --git a/libc/thread/yield.c b/libc/thread/yield.c index af0c9dba0da..710045b2843 100644 --- a/libc/thread/yield.c +++ b/libc/thread/yield.c @@ -16,8 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/thread/yield.h" #include "libc/calls/calls.h" +#include "libc/thread/yield.h" int cthread_yield(void) { return sched_yield(); diff --git a/libc/time/localtime.c b/libc/time/localtime.c index 6fdcb0a2981..1ccad692bf9 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -1171,11 +1171,11 @@ tzsetwall(void) if (lcl_is_set < 0) return; lcl_is_set = -1; - #ifdef ALL_STATE if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); - if (lclptr == NULL) { + if ((lclptr = malloc(sizeof(*lclptr)))) { + __cxa_atexit(free, lclptr, 0); + } else { settzname(); /* all we can do */ return; } @@ -1211,8 +1211,9 @@ tzset(void) #ifdef ALL_STATE if (lclptr == NULL) { - lclptr = (struct state *) malloc(sizeof *lclptr); - if (lclptr == NULL) { + if ((lclptr = malloc(sizeof(*lclptr)))) { + __cxa_atexit(free, lclptr, 0); + } else { settzname(); /* all we can do */ return; } @@ -1372,8 +1373,11 @@ gmtsub( if (!gmt_is_set) { gmt_is_set = TRUE; #ifdef ALL_STATE - gmtptr = (struct state *) malloc(sizeof *gmtptr); - if (gmtptr != NULL) + if (!gmtptr) { + gmtptr = malloc(sizeof(*gmtptr)); + __cxa_atexit(free, gmtptr, 0); + } + if (gmtptr) #endif /* defined ALL_STATE */ gmtload(gmtptr); } diff --git a/libc/zipos/close.c b/libc/zipos/close.c index e0af6b25d0e..bffb0416c0d 100644 --- a/libc/zipos/close.c +++ b/libc/zipos/close.c @@ -31,7 +31,8 @@ int __zipos_close(int fd) { struct ZiposHandle *h; h = (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle; - ZTRACE("__zipos_close(%S)", ZIP_CFILE_NAMESIZE(__zipos_get()->map + h->cfile), + ZTRACE("__zipos_close(%.*s)", + ZIP_CFILE_NAMESIZE(__zipos_get()->map + h->cfile), ZIP_CFILE_NAME(__zipos_get()->map + h->cfile)); if (!IsWindows()) { sys_close(fd); diff --git a/libc/zipos/fcntl.c b/libc/zipos/fcntl.c index c21d5633e2c..1fd82e2aa7a 100644 --- a/libc/zipos/fcntl.c +++ b/libc/zipos/fcntl.c @@ -29,7 +29,7 @@ int __zipos_fcntl(int fd, int cmd, uintptr_t arg) { if (cmd == F_GETFD) { - ZTRACE("__zipos_fcntl(%S, %s)", + ZTRACE("__zipos_fcntl(%.*s, %s)", ZIP_CFILE_NAMESIZE(ZIPOS->map + HANDLE->cfile), ZIP_CFILE_NAME(ZIPOS->map + HANDLE->cfile), "F_GETFD"); if (g_fds.p[fd].flags & O_CLOEXEC) { @@ -38,7 +38,7 @@ int __zipos_fcntl(int fd, int cmd, uintptr_t arg) { return 0; } } else if (cmd == F_SETFD) { - ZTRACE("__zipos_fcntl(%S, %s, 0x%x)", + ZTRACE("__zipos_fcntl(%.*s, %s, 0x%x)", ZIP_CFILE_NAMESIZE(ZIPOS->map + HANDLE->cfile), ZIP_CFILE_NAME(ZIPOS->map + HANDLE->cfile), "F_SETFD", arg); if (arg & FD_CLOEXEC) { @@ -49,7 +49,7 @@ int __zipos_fcntl(int fd, int cmd, uintptr_t arg) { return 0; } } else { - ZTRACE("__zipos_fcntl(%S, %d, 0x%x) → EINVAL", + ZTRACE("__zipos_fcntl(%.*s, %d, 0x%x) → EINVAL", ZIP_CFILE_NAMESIZE(ZIPOS->map + HANDLE->cfile), ZIP_CFILE_NAME(ZIPOS->map + HANDLE->cfile), cmd, arg); return einval(); diff --git a/libc/zipos/fstat.c b/libc/zipos/fstat.c index 3a1fea6bec0..c0c2a708515 100644 --- a/libc/zipos/fstat.c +++ b/libc/zipos/fstat.c @@ -31,7 +31,7 @@ int __zipos_fstat(const struct ZiposHandle *h, struct stat *st) { int rc; if (!st) return efault(); if (!(rc = __zipos_stat_impl(__zipos_get(), h->cfile, st))) { - ZTRACE("__zipos_fstat(%S) → %d", + ZTRACE("__zipos_fstat(%.*s) → %d", ZIP_CFILE_NAMESIZE(__zipos_get()->map + h->cfile), ZIP_CFILE_NAME(__zipos_get()->map + h->cfile), st->st_size); return 0; diff --git a/libc/zipos/lseek.c b/libc/zipos/lseek.c index 9d36c88e037..87693b3aa1e 100644 --- a/libc/zipos/lseek.c +++ b/libc/zipos/lseek.c @@ -48,7 +48,7 @@ int64_t __zipos_lseek(struct ZiposHandle *h, int64_t offset, unsigned whence) { return einval(); } h->pos = i; - ZTRACE("__zipos_lseek(%S, %d)", + ZTRACE("__zipos_lseek(%.*s, %d)", ZIP_CFILE_NAMESIZE(__zipos_get()->map + h->cfile), ZIP_CFILE_NAME(__zipos_get()->map + h->cfile), i); return i; diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 917955d43ee..4582be7ab07 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -155,14 +155,14 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) { if ((zipos = __zipos_get())) { if ((cf = __zipos_find(zipos, name)) != -1) { fd = __zipos_load(zipos, cf, flags, mode); - ZTRACE("__zipos_open(%S)", name->len, name->path); + ZTRACE("__zipos_open(%.*s)", name->len, name->path); } else { - ZTRACE("__zipos_open(%S) enoent", name->len, name->path); + ZTRACE("__zipos_open(%.*s) enoent", name->len, name->path); fd = enoent(); } } else { fd = enoexec(); - ZTRACE("__zipos_open(%S) enoexec", name->len, name->path); + ZTRACE("__zipos_open(%.*s) enoexec", name->len, name->path); } return fd; } diff --git a/libc/zipos/read.c b/libc/zipos/read.c index 698d3a85f1b..47f7e18fe3a 100644 --- a/libc/zipos/read.c +++ b/libc/zipos/read.c @@ -46,7 +46,7 @@ ssize_t __zipos_read(struct ZiposHandle *h, const struct iovec *iov, memcpy(iov[i].iov_base, h->mem + y, b); } if (opt_offset == -1) h->pos = y; - ZTRACE("__zipos_read(%S, cap=%d, off=%d) -> %d", + ZTRACE("__zipos_read(%.*s, cap=%d, off=%d) -> %d", ZIP_CFILE_NAMESIZE(__zipos_get()->map + h->cfile), ZIP_CFILE_NAME(__zipos_get()->map + h->cfile), GetIovSize(iov, iovlen), x, y - x); diff --git a/libc/zipos/stat.c b/libc/zipos/stat.c index dbb937fba36..6be580a8b97 100644 --- a/libc/zipos/stat.c +++ b/libc/zipos/stat.c @@ -35,11 +35,11 @@ int __zipos_stat(const struct ZiposUri *name, struct stat *st) { if ((cf = __zipos_find(zipos, name)) != -1) { return __zipos_stat_impl(zipos, cf, st); } else { - ZTRACE("__zipos_stat(%S) -> enoent", name->len, name->path); + ZTRACE("__zipos_stat(%.*s) -> enoent", name->len, name->path); return enoent(); } } else { - ZTRACE("__zipos_stat(%S) → enoexec", name->len, name->path); + ZTRACE("__zipos_stat(%.*s) → enoexec", name->len, name->path); return enoexec(); } } diff --git a/test/libc/alg/arraylist_test.c b/test/libc/alg/arraylist_test.c index 50e426f5cc5..5d12ac1a6cd 100644 --- a/test/libc/alg/arraylist_test.c +++ b/test/libc/alg/arraylist_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/alg.h" #include "libc/alg/arraylist.internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" @@ -42,7 +43,9 @@ TEST(append, worksGreatForScalars) { char c = 'a'; struct string s; memset(&s, 0, sizeof(s)); - for (size_t i = 0; i < 1024; ++i) ASSERT_EQ(i, append(&s, &c)); + for (size_t i = 0; i < 1024; ++i) { + ASSERT_EQ(i, append(&s, &c)); + } ASSERT_EQ(1024, s.i); for (size_t i = 0; i < s.i; ++i) ASSERT_EQ('a', s.p[i]); free_s(&s.p); @@ -52,10 +55,14 @@ TEST(append, isGenericallyTyped) { int c = 0x31337; struct ArrayListInteger s; memset(&s, 0, sizeof(s)); - for (size_t i = 0; i < 1024; ++i) ASSERT_EQ(i, append(&s, &c)); + for (size_t i = 0; i < 1024; ++i) { + ASSERT_EQ(i, append(&s, &c)); + } ASSERT_EQ(1024, s.i); ASSERT_GT(malloc_usable_size(s.p), 1024 * sizeof(int)); - for (size_t i = 0; i < s.i; ++i) ASSERT_EQ(0x31337, s.p[i]); + for (size_t i = 0; i < s.i; ++i) { + ASSERT_EQ(0x31337, s.p[i]); + } free_s(&s.p); } diff --git a/test/libc/alg/replacestr_test.c b/test/libc/alg/replacestr_test.c index e7721c987cb..804bc03be0c 100644 --- a/test/libc/alg/replacestr_test.c +++ b/test/libc/alg/replacestr_test.c @@ -18,22 +18,24 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/alg.h" #include "libc/errno.h" +#include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" TEST(replacestr, demo) { - EXPECT_STREQ("hello friends", replacestr("hello world", "world", "friends")); - EXPECT_STREQ("bbbbbbbb", replacestr("aaaa", "a", "bb")); + EXPECT_STREQ("hello friends", + gc(replacestr("hello world", "world", "friends"))); + EXPECT_STREQ("bbbbbbbb", gc(replacestr("aaaa", "a", "bb"))); } TEST(replacestr, emptyString) { - EXPECT_STREQ("", replacestr("", "x", "y")); + EXPECT_STREQ("", gc(replacestr("", "x", "y"))); } TEST(replacestr, emptyNeedle) { - EXPECT_EQ(NULL, replacestr("a", "", "a")); + EXPECT_EQ(NULL, gc(replacestr("a", "", "a"))); EXPECT_EQ(EINVAL, errno); } TEST(replacestr, needleInReplacement_doesntExplode) { - EXPECT_STREQ("xxxxxxx", replacestr("x", "x", "xxxxxxx")); + EXPECT_STREQ("xxxxxxx", gc(replacestr("x", "x", "xxxxxxx"))); } diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index f2b7a4a5887..e4f6da7a792 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -49,6 +49,7 @@ void SetUp(void) { void TearDown(void) { CHECK_NE(-1, setenv("PATH", oldpath, true)); + free(oldpath); } TEST(commandv, testPathSearch) { diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index 2b27a3954ce..bd373fd765d 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -63,8 +63,8 @@ TEST(mkntcmdline, basicQuoting) { TEST(mkntcmdline, testUnicode) { char *argv1[] = { - strdup("(╯°□°)╯"), - strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非"), + gc(strdup("(╯°□°)╯")), + gc(strdup("要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非")), NULL, }; EXPECT_NE(-1, mkntcmdline(cmdline, argv1[0], argv1)); diff --git a/test/libc/calls/mprotect_test.c b/test/libc/calls/mprotect_test.c index 7857f106642..8cf332040f6 100644 --- a/test/libc/calls/mprotect_test.c +++ b/test/libc/calls/mprotect_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sa.h" diff --git a/test/libc/calls/readansi_test.c b/test/libc/calls/readansi_test.c index 2fb96cf3ac8..4c4b10d60f1 100644 --- a/test/libc/calls/readansi_test.c +++ b/test/libc/calls/readansi_test.c @@ -77,3 +77,17 @@ TEST(readansi, testOperatingSystemCommand) { ASSERT_TRUE(WIFEXITED(ws)); ASSERT_EQ(0, WEXITSTATUS(ws)); } + +TEST(readansi, testSqueeze) { + int fds[2]; + char b[32]; + const char *s = "\e\\"; + ASSERT_NE(-1, pipe(fds)); + write(fds[1], s, strlen(s)); + close(fds[1]); + EXPECT_EQ(strlen(s), readansi(fds[0], b, sizeof(b))); + EXPECT_STREQ(s, b); + EXPECT_EQ(0, readansi(fds[0], b, sizeof(b))); + EXPECT_STREQ("", b); + close(fds[0]); +} diff --git a/test/libc/calls/signal_test.c b/test/libc/calls/signal_test.c index 8481166aa23..3c1330b098b 100644 --- a/test/libc/calls/signal_test.c +++ b/test/libc/calls/signal_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/log/check.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" diff --git a/test/libc/calls/writev_test.c b/test/libc/calls/writev_test.c index ac7c999805f..8d256b4f540 100644 --- a/test/libc/calls/writev_test.c +++ b/test/libc/calls/writev_test.c @@ -21,6 +21,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/macros.internal.h" +#include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/sysv/consts/auxv.h" @@ -47,9 +48,9 @@ TEST(writev, test) { TEST(writev, big_fullCompletion) { int fd; - char *ba = malloc(2 * 1024 * 1024); - char *bb = malloc(2 * 1024 * 1024); - char *bc = malloc(2 * 1024 * 1024); + char *ba = gc(malloc(2 * 1024 * 1024)); + char *bb = gc(malloc(2 * 1024 * 1024)); + char *bc = gc(malloc(2 * 1024 * 1024)); struct iovec iov[] = { {"", 0}, // {ba, 2 * 1024 * 1024}, // diff --git a/test/libc/dns/prototxt_test.c b/test/libc/dns/prototxt_test.c index d3c4579261f..9b18c183478 100644 --- a/test/libc/dns/prototxt_test.c +++ b/test/libc/dns/prototxt_test.c @@ -25,22 +25,21 @@ │ OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dns/prototxt.h" - #include "libc/calls/calls.h" #include "libc/dns/dns.h" #include "libc/dns/ent.h" +#include "libc/dns/prototxt.h" +#include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" char testlib_enable_tmp_setup_teardown; void SetUp() { int fd; - const char* sample = "\ + const char *sample = "\ # skip comment string\n\ rspf 73 RSPF CPHB \n\ ggp 3 GGP "; - ASSERT_NE(-1, (fd = creat("protocols", 0755))); ASSERT_NE(-1, write(fd, sample, strlen(sample))); ASSERT_NE(-1, close(fd)); @@ -49,15 +48,12 @@ ggp 3 GGP "; TEST(LookupProtoByNumber, GetNameWhenNumberCorrect) { char name[16]; /* sample has only names of length 3-4 */ strcpy(name, ""); - ASSERT_EQ(-1, /*non-existent number */ LookupProtoByNumber(24, name, sizeof(name), "protocols")); - ASSERT_EQ(-1, /* sizeof(name) insufficient, memccpy failure */ LookupProtoByNumber(73, name, 1, "protocols")); ASSERT_STREQ(name, ""); /* cleaned up after memccpy failed */ - - ASSERT_EQ(0, /* works with valid number */ + ASSERT_EQ(0, /* works with valid number */ LookupProtoByNumber(73, name, sizeof(name), "protocols")); ASSERT_STREQ(name, "rspf"); /* official name written */ } @@ -65,18 +61,14 @@ TEST(LookupProtoByNumber, GetNameWhenNumberCorrect) { TEST(LookupProtoByName, GetNumberWhenNameOrAlias) { char name[16]; /* sample has only names of length 3-4 */ strcpy(name, ""); - ASSERT_EQ(-1, /* non-existent name or alias */ LookupProtoByName("tcp", name, sizeof(name), "protocols")); - ASSERT_EQ(-1, /* sizeof(name) insufficient, memccpy failure */ LookupProtoByName("ggp", name, 1, "protocols")); ASSERT_STREQ(name, ""); /* cleaned up after memccpy failed */ - - ASSERT_EQ(3, /* works with valid name */ + ASSERT_EQ(3, /* works with valid name */ LookupProtoByName("ggp", name, sizeof(name), "protocols")); ASSERT_STREQ(name, "ggp"); - ASSERT_EQ(73, /* works with valid alias */ LookupProtoByName("CPHB", name, sizeof(name), "protocols")); ASSERT_STREQ(name, "rspf"); /* official name written */ diff --git a/test/libc/fmt/atoi_test.c b/test/libc/fmt/atoi_test.c index b7d808cd04b..0b491c9dc86 100644 --- a/test/libc/fmt/atoi_test.c +++ b/test/libc/fmt/atoi_test.c @@ -25,6 +25,7 @@ TEST(atoi, test) { EXPECT_EQ(0, atoi("")); + EXPECT_EQ(0, atoi("-b")); EXPECT_EQ(0, atoi("0")); EXPECT_EQ(1, atoi("1")); EXPECT_EQ(9, atoi("9")); diff --git a/test/libc/fmt/dirname_test.c b/test/libc/fmt/dirname_test.c index 5b512be0c93..02542b87e5c 100644 --- a/test/libc/fmt/dirname_test.c +++ b/test/libc/fmt/dirname_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" +#include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/fmt/fcvt_test.c b/test/libc/fmt/fcvt_test.c index 1e6997e4753..013e96640ff 100644 --- a/test/libc/fmt/fcvt_test.c +++ b/test/libc/fmt/fcvt_test.c @@ -23,7 +23,8 @@ TEST(fcvt, test) { int decpt, sign; - ASSERT_STREQ("3.14159265358979", xasprintf("%.14f", 3.14159265358979323846)); + ASSERT_STREQ("3.14159265358979", + gc(xasprintf("%.14f", 3.14159265358979323846))); ASSERT_STREQ("3141592653589793", fcvt(3.14159265358979323846, 15, &decpt, &sign)); ASSERT_EQ(1, decpt); diff --git a/test/libc/fmt/fmt_test.c b/test/libc/fmt/fmt_test.c index 0e02e489a1a..9a3851dfb18 100644 --- a/test/libc/fmt/fmt_test.c +++ b/test/libc/fmt/fmt_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/fmt.h" #include "libc/limits.h" +#include "libc/log/log.h" #include "libc/math.h" #include "libc/runtime/gc.internal.h" #include "libc/str/str.h" diff --git a/test/libc/fmt/formatint64thousands_test.c b/test/libc/fmt/formatint64thousands_test.c index f5d441a8e42..c0158d2a812 100644 --- a/test/libc/fmt/formatint64thousands_test.c +++ b/test/libc/fmt/formatint64thousands_test.c @@ -16,8 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/limits.h" +#include "libc/macros.internal.h" +#include "libc/math.h" +#include "libc/nexgen32e/bsr.h" #include "libc/stdio/stdio.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" diff --git a/test/libc/fmt/lengthuint64_test.c b/test/libc/fmt/lengthuint64_test.c new file mode 100644 index 00000000000..f0ddbd48f90 --- /dev/null +++ b/test/libc/fmt/lengthuint64_test.c @@ -0,0 +1,102 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/itoa.h" +#include "libc/limits.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +TEST(LengthUint64Thousands, test) { + int i; + char s[27]; + static const uint64_t v[] = { + 9, + 10, + 11, + 99, + 100, + 101, + 999, + 1000, + 1001, + 999999, + 1000000, + 1000001, + UINT64_MIN, + UINT64_MAX, + UINT64_MIN + 1, + UINT64_MAX - 1, + 0x8000000000000001ull, + 0x8000000000000000ull, + }; + for (i = 0; i < ARRAYLEN(v); ++i) { + FormatUint64Thousands(s, v[i]); + ASSERT_EQ(strlen(s), LengthUint64Thousands(v[i]), "%,lu", v[i]); + } +} + +TEST(LengthInt64Thousands, test) { + int i; + char s[27]; + static const int64_t v[] = { + -1000001, + -1000000, + -999999, + -1001, + -1000, + -999, + -101, + -100, + -99, + -11, + -10, + -9, + -1, + 0, + 1, + 9, + 10, + 11, + 99, + 100, + 101, + 999, + 1000, + 1001, + 999999, + 1000000, + 1000001, + INT64_MIN, + INT64_MAX, + INT64_MIN + 1, + INT64_MAX - 1, + }; + for (i = 0; i < ARRAYLEN(v); ++i) { + FormatInt64Thousands(s, v[i]); + ASSERT_EQ(strlen(s), LengthInt64Thousands(v[i]), "%,ld\n\t%s\n\t%lx", v[i], + s, v[i]); + } +} + +BENCH(LengthInt64, bench) { + EZBENCH2("LengthInt64", donothing, LengthInt64(INT64_MIN)); + EZBENCH2("LengthUint64", donothing, LengthUint64(UINT64_MAX)); + EZBENCH2("LengthInt64Thousands", donothing, LengthInt64Thousands(INT64_MIN)); + EZBENCH2("LengthUint64Thousands", donothing, + LengthUint64Thousands(UINT64_MAX)); +} diff --git a/test/libc/fmt/llog10_test.c b/test/libc/fmt/mapdoserrortoerrno_test.c similarity index 79% rename from test/libc/fmt/llog10_test.c rename to test/libc/fmt/mapdoserrortoerrno_test.c index 431544e402c..9dd047fef39 100644 --- a/test/libc/fmt/llog10_test.c +++ b/test/libc/fmt/mapdoserrortoerrno_test.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -16,20 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/bits.h" -#include "libc/fmt/conv.h" -#include "libc/limits.h" +#include "libc/errno.h" +#include "libc/nt/errors.h" +#include "libc/sock/internal.h" +#include "libc/sock/sock.h" #include "libc/testlib/testlib.h" -TEST(llog10, test) { - EXPECT_EQ(0, llog10(1)); - EXPECT_EQ(0, llog10(2)); - EXPECT_EQ(0, llog10(9)); - EXPECT_EQ(1, llog10(10)); - EXPECT_EQ(1, llog10(11)); - EXPECT_EQ(1, llog10(99)); - EXPECT_EQ(2, llog10(100)); - EXPECT_EQ(2, llog10(101)); - EXPECT_EQ(9, llog10(INT_MAX)); - EXPECT_EQ(12, llog10(1000000000000)); +TEST(__dos2errno, test) { + EXPECT_EQ(EACCES, __dos2errno(kNtErrorSectorNotFound)); + EXPECT_EQ(EADDRNOTAVAIL, __dos2errno(kNtErrorInvalidNetname)); } diff --git a/test/libc/fmt/palandprintf_test.c b/test/libc/fmt/palandprintf_test.c index dfeda56bad5..fd2d24c4df3 100644 --- a/test/libc/fmt/palandprintf_test.c +++ b/test/libc/fmt/palandprintf_test.c @@ -623,6 +623,12 @@ TEST(palandprintf, precisionStillRespectsNulTerminatorIfNotEscOrRepr) { Format("%.20s - %d lines %s", "Makefile", 25, "")); } +TEST(sprintf, commas) { + char buf[64]; + sprintf(buf, "%,d", 123456789); + ASSERT_STREQ("123,456,789", buf); +} + BENCH(palandprintf, bench) { EZBENCH2("ascii", donothing, Format(VEIL("r", "hiuhcreohucreo"))); EZBENCH2("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo"))); @@ -632,11 +638,23 @@ BENCH(palandprintf, bench) { EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯"))); EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23))); EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23))); + EZBENCH2("%f M_PI", donothing, Format("%f", VEIL("x", M_PI))); + EZBENCH2("%g M_PI", donothing, Format("%g", VEIL("x", M_PI))); + EZBENCH2("%a M_PI", donothing, Format("%a", VEIL("x", M_PI))); + EZBENCH2("%e M_PI", donothing, Format("%e", VEIL("x", M_PI))); EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); - EZBENCH2("LONG_MIN %x", donothing, Format("%lx", VEIL("r", LONG_MIN))); - EZBENCH2("LONG_MIN %d", donothing, Format("%ld", VEIL("r", LONG_MIN))); - EZBENCH2("23 int64toarray", donothing, int64toarray_radix10(23, buffer)); - EZBENCH2("INT_MIN int64toarray", donothing, + EZBENCH2("INT_MIN %,d", donothing, Format("%,d", VEIL("r", INT_MIN))); + EZBENCH2("INT_MIN %ld", donothing, Format("%ld", (long)VEIL("r", INT_MIN))); + EZBENCH2("INT_MIN %jd", donothing, + Format("%jd", (intmax_t)VEIL("r", INT_MIN))); + EZBENCH2("LONG_MIN %lx", donothing, Format("%lx", VEIL("r", LONG_MIN))); + EZBENCH2("LONG_MIN %ld", donothing, Format("%ld", VEIL("r", LONG_MIN))); + EZBENCH2("LONG_MIN %jd", donothing, + Format("%jd", (intmax_t)VEIL("r", LONG_MIN))); + EZBENCH2("LONG_MIN %jx", donothing, + Format("%jx", (intmax_t)VEIL("r", LONG_MIN))); + EZBENCH2("int64toarray 23", donothing, int64toarray_radix10(23, buffer)); + EZBENCH2("int64toarray min", donothing, int64toarray_radix10(INT_MIN, buffer)); } diff --git a/test/libc/fmt/strerror_test.c b/test/libc/fmt/strerror_r_test.c similarity index 100% rename from test/libc/fmt/strerror_test.c rename to test/libc/fmt/strerror_r_test.c diff --git a/test/libc/fmt/test.mk b/test/libc/fmt/test.mk index 808b76c3bc2..57c9c3262fd 100644 --- a/test/libc/fmt/test.mk +++ b/test/libc/fmt/test.mk @@ -24,13 +24,14 @@ TEST_LIBC_FMT_DIRECTDEPS = \ LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ + LIBC_RAND \ LIBC_RUNTIME \ LIBC_STDIO \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV \ - LIBC_TINYMATH \ LIBC_TESTLIB \ + LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_X \ THIRD_PARTY_GDTOA diff --git a/test/libc/intrin/asan_test.c b/test/libc/intrin/asan_test.c index dfe9c78e810..46bb2c0ea93 100644 --- a/test/libc/intrin/asan_test.c +++ b/test/libc/intrin/asan_test.c @@ -19,6 +19,7 @@ #include "libc/dce.h" #include "libc/fmt/fmt.h" #include "libc/intrin/asan.internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -83,8 +84,8 @@ BENCH(asan, bench) { if (!IsAsan()) return; m = 4 * 1024 * 1024; p = gc(malloc(m)); - EZBENCH_N("__asan_check", 0, __asan_check(p, 0)); + EZBENCH_N("__asan_check", 0, EXPROPRIATE(__asan_check(p, 0).kind)); for (n = 2; n <= m; n *= 2) { - EZBENCH_N("__asan_check", n, __asan_check(p, n)); + EZBENCH_N("__asan_check", n, EXPROPRIATE(__asan_check(p, n).kind)); } } diff --git a/test/libc/intrin/memset_test.c b/test/libc/intrin/memset_test.c index 7edaa9fa9c2..b1a3ee8b923 100644 --- a/test/libc/intrin/memset_test.c +++ b/test/libc/intrin/memset_test.c @@ -27,7 +27,7 @@ static noasan void *golden(void *p, int c, size_t n) { size_t i; - if (IsAsan()) __asan_check(p, n); + if (IsAsan()) __asan_verify(p, n); for (i = 0; i < n; ++i) ((char *)p)[i] = c; return p; } @@ -35,8 +35,8 @@ static noasan void *golden(void *p, int c, size_t n) { TEST(memset, hug) { char *a, *b; int i, j, c; - a = malloc(1025 * 2); - b = malloc(1025 * 2); + a = gc(malloc(1025 * 2)); + b = gc(malloc(1025 * 2)); for (i = 0; i < 1025; ++i) { for (j = 0; j < 1025 - i; ++j) { c = vigna(); @@ -52,8 +52,8 @@ TEST(memset, hug) { TEST(bzero, hug) { char *a, *b; int i, j; - a = malloc(1025 * 2); - b = malloc(1025 * 2); + a = gc(malloc(1025 * 2)); + b = gc(malloc(1025 * 2)); for (i = 0; i < 1025; ++i) { for (j = 0; j < 1025 - i; ++j) { rngset(a, i + j, 0, 0); diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index 45bd564529f..4e7c0ea185b 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -17,11 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/intrin/asan.internal.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" #include "libc/stdio/append.internal.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -31,9 +36,12 @@ #include "libc/x/x.h" #include "net/http/escape.h" -typedef char xmm_t __attribute__((__vector_size__(16))); +static bool OutputHasSymbol(const char *output, const char *s) { + return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL")); +} -noinline void ThisIsAnFpeCrash(void) { +void FpuCrash(void) { + typedef char xmm_t __attribute__((__vector_size__(16))); xmm_t v = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; volatile int x = 0; @@ -43,11 +51,241 @@ noinline void ThisIsAnFpeCrash(void) { fputc(7 / x, stdout); } +char bss[10]; +void BssOverrunCrash(int n) { + int i; + for (i = 0; i < n; ++i) { + bss[i] = i; + } +} + +char data[10] = "abcdeabcde"; +void DataOverrunCrash(int n) { + int i; + for (i = 0; i < n; ++i) { + data[i] = i; + } +} + +const char rodata[10] = "abcdeabcde"; +int RodataOverrunCrash(int i) { + return rodata[i]; +} + +char *StackOverrunCrash(int n) { + int i; + char stack[10]; + bzero(stack, sizeof(stack)); + for (i = 0; i < n; ++i) { + stack[i] = i; + } + return strdup(stack); +} + +char *MemoryLeakCrash(void) { + char *p = strdup("doge"); + testlib_checkformemoryleaks(); + return p; +} + +int NpeCrash(char *p) { + return *p; +} + +void (*pFpuCrash)(void) = FpuCrash; +void (*pBssOverrunCrash)(int) = BssOverrunCrash; +void (*pDataOverrunCrash)(int) = DataOverrunCrash; +int (*pRodataOverrunCrash)(int) = RodataOverrunCrash; +char *(*pStackOverrunCrash)(int) = StackOverrunCrash; +char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash; +int (*pNpeCrash)(char *) = NpeCrash; + void SetUp(void) { ShowCrashReports(); - if (__argc == 2 && !strcmp(__argv[1], "1")) { - ThisIsAnFpeCrash(); + if (__argc == 2) { + switch (atoi(__argv[1])) { + case 0: + break; + case 1: + pFpuCrash(); + exit(0); + case 2: + pBssOverrunCrash(10 + 1); + exit(0); + case 3: + exit(pRodataOverrunCrash(10 + 1)); + case 4: + pDataOverrunCrash(10 + 1); + exit(0); + case 5: + exit((intptr_t)pStackOverrunCrash(10 + 1)); + case 6: + exit((intptr_t)pMemoryLeakCrash()); + case 7: + exit(pNpeCrash(0)); + default: + printf("preventing fork recursion: %s\n", __argv[1]); + exit(1); + } + } +} + +// UNFREED MEMORY +// o/dbg/test/libc/log/backtrace_test.com +// max allocated space 655,360 +// total allocated space 80 +// total free space 327,600 +// releasable space 0 +// mmaped space 65,360 +// non-mmapped space 327,680 +// +// 100080040020 64 bytes 5 used +// 421871 strdup +// 416529 MemoryLeakCrash +// 41666d SetUp +// 45428c testlib_runtestcases +// +// 00007fff0000-000080010000 rw-pa-F 2x shadow of 000000000000 +// 000080070000-0000800a0000 rw-pa-F 3x shadow of 0000003c0000 +// 02008fff0000-020090020000 rw-pa-F 3x shadow of 10007ffc0000 +// 020090060000-020090080000 rw-pa-F 2x shadow of 100080340000 +// 0e007fff0000-0e0080010000 rw-pa-F 2x shadow of 6ffffffc0000 +// 100006560000-100006580000 rw-pa-F 2x shadow of 7ffc32b40000 +// 100080000000-100080050000 rw-pa-- 5x automap w/ 50 frame hole +// 100080370000-100080390000 rw-pa-- 2x automap w/ 1 frame hole +// 1000803a0000-1000803b0000 rw-pa-- 1x automap +// 6ffffffe0000-700000000000 rw-paSF 2x stack +// # 24 frames mapped w/ 51 frames gapped +TEST(ShowCrashReports, testMemoryLeakCrash) { + size_t got; + ssize_t rc; + int ws, pid, fds[2]; + char *output, buf[512]; + if (!IsAsan()) { + /* TODO(jart): How can we make this work without ASAN? */ + return; + } + ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + dup2(fds[1], 1); + dup2(fds[1], 2); + execv(program_executable_name, + (char *const[]){program_executable_name, "6", 0}); + _exit(127); + } + close(fds[1]); + output = 0; + appends(&output, ""); + for (;;) { + rc = read(fds[0], buf, sizeof(buf)); + if (rc == -1) { + ASSERT_EQ(EINTR, errno); + continue; + } + if ((got = rc)) { + appendd(&output, buf, got); + } else { + break; + } + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(78, WEXITSTATUS(ws)); + if (!strstr(output, "UNFREED MEMORY")) { + fprintf(stderr, "ERROR: crash report didn't report leak\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (IsAsan()) { + if (!OutputHasSymbol(output, "strdup") || + !OutputHasSymbol(output, "MemoryLeakCrash")) { + fprintf(stderr, "ERROR: crash report didn't backtrace allocation\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } } + free(output); +} + +// clang-format off +// asan error: stack overrun 1-byte store at 0x6fffffffff0a shadow 0x0e007fff7fe1 +// x +// uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu..........oooooooooooooooooooooo.................. +// |-15 |-15 |-15 |0 |2 |-13 |-13 |0 |0 +// ╡A    ΦCE     └eA              ☺☻♥♦♣♠•◘○○0000003fffffffff◙CapAmb:○0000↑ÿ_╟ⁿ⌂  ÿ  +// 000000400000-000000462000 .text 000000462000-00000046a000 .data +// 00007fff0000-00008000ffff +// 000080070000-00008009ffff +// 02008fff0000-02009001ffff +// 020090060000-02009007ffff +// 0e007ffb0000-0e008000ffff ←shadow +// 100018eb0000-100018ecffff +// 100080000000-10008009ffff +// 100080360000-10008037ffff +// 100080390000-10008039ffff +// 6fffffe00000-6fffffffffff ←address +// 0x0000000000407c06: __die at libc/log/die.c:37 +// 0x000000000040b1c1: __asan_report_store at libc/intrin/asan.c:1104 +// 0x0000000000443302: __asan_report_store1 at libc/intrin/somanyasan.S:118 +// 0x000000000041669a: StackOverrunCrash at test/libc/log/backtrace_test.c:76 +// 0x00000000004167e7: SetUp at test/libc/log/backtrace_test.c:105 +// 0x0000000000452d4b: testlib_runtestcases at libc/testlib/testrunner.c:98 +// 0x000000000044c740: testlib_runalltests at libc/testlib/runner.c:37 +// 0x00000000004026db: main at libc/testlib/testmain.c:155 +// 0x000000000040324f: cosmo at libc/runtime/cosmo.S:64 +// 0x000000000040219b: _start at libc/crt/crt.S:67 +// clang-format on +TEST(ShowCrashReports, testStackOverrunCrash) { + if (!IsAsan()) return; + size_t got; + ssize_t rc; + int ws, pid, fds[2]; + char *output, buf[512]; + ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + dup2(fds[1], 1); + dup2(fds[1], 2); + execv(program_executable_name, + (char *const[]){program_executable_name, "5", 0}); + _exit(127); + } + close(fds[1]); + output = 0; + appends(&output, ""); + for (;;) { + rc = read(fds[0], buf, sizeof(buf)); + if (rc == -1) { + ASSERT_EQ(EINTR, errno); + continue; + } + if ((got = rc)) { + appendd(&output, buf, got); + } else { + break; + } + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(77, WEXITSTATUS(ws)); + /* NULL is stopgap until we can copy symbol tablces into binary */ + if (!OutputHasSymbol(output, "StackOverrunCrash")) { + fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!strstr(output, "☺☻♥♦♣♠•◘○")) { + fprintf(stderr, "ERROR: crash report didn't have memory diagram\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!strstr(output, "stack overrun")) { + fprintf(stderr, "ERROR: crash report misclassified stack overrun\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + free(output); } // error: Uncaught SIGFPE (FPE_INTDIV) on nightmare pid 11724 @@ -55,7 +293,7 @@ void SetUp(void) { // ENOTTY[25] // Linux nightmare SMP Thu, 12 Aug 2021 06:16:45 UTC // -// 0x0000000000414659: ThisIsAnFpeCrash at test/libc/log/backtrace_test.c:35 +// 0x0000000000414659: FpuCrash at test/libc/log/backtrace_test.c:35 // 0x000000000045003b: testlib_runtestcases at libc/testlib/testrunner.c:98 // 0x000000000044b770: testlib_runalltests at libc/testlib/runner.c:37 // 0x000000000040278e: main at libc/testlib/testmain.c:86 @@ -102,7 +340,6 @@ void SetUp(void) { // 7ffe075ab000-7ffe075ac000 r-xp 00000000 00:00 0 [vdso] // // /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.com.tmp.11721 1 - TEST(ShowCrashReports, testDivideByZero) { size_t got; ssize_t rc; @@ -135,43 +372,242 @@ TEST(ShowCrashReports, testDivideByZero) { ASSERT_NE(-1, wait(&ws)); EXPECT_TRUE(WIFEXITED(ws)); EXPECT_EQ(128 + SIGFPE, WEXITSTATUS(ws)); - /* NULL is stopgap until we can copy symbol tablces into binary */ - if (!strstr(output, "ThisIsAnFpeCrash") && !strstr(output, "NULL")) { + if (!OutputHasSymbol(output, "FpuCrash")) { fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n", gc(IndentLines(output, -1, 0, 4))); - exit(1); + __die(); } - if (!strstr(output, gc(xasprintf("%d", pid)))) { fprintf(stderr, "ERROR: crash report didn't have pid\n%s\n", gc(IndentLines(output, -1, 0, 4))); - exit(1); + __die(); } - if (!strstr(output, "SIGFPE")) { fprintf(stderr, "ERROR: crash report didn't have signal name\n%s\n", gc(IndentLines(output, -1, 0, 4))); - exit(1); + __die(); } - if (!strstr(output, "3.141")) { fprintf(stderr, "ERROR: crash report didn't have fpu register\n%s\n", gc(IndentLines(output, -1, 0, 4))); - exit(1); + __die(); } - if (!strstr(output, "0f0e0d0c0b0a09080706050403020100")) { fprintf(stderr, "ERROR: crash report didn't have sse register\n%s\n", gc(IndentLines(output, -1, 0, 4))); - exit(1); + __die(); } - if (!strstr(output, "3133731337")) { fprintf(stderr, "ERROR: crash report didn't have general register\n%s\n", gc(IndentLines(output, -1, 0, 4))); - exit(1); + __die(); } + free(output); +} +// clang-format off +// asan error: global redzone 1-byte store at 0x00000048cf2a shadow 0x0000800899e5 +// x +// ........................................OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO +// |0 |0 |0 |0 |2 |-6 |-6 |-6 |-6 +//                                ☺☻♥♦♣♠•◘○                                         +// 000000400000-000000460000 .text +// 000000460000-000000468000 .data +// 00007fff0000-00008000ffff +// 000080070000-00008009ffff ←shadow +// 02008fff0000-02009001ffff +// 020090060000-02009007ffff +// 0e007ffb0000-0e008000ffff +// 1000286b0000-1000286cffff +// 100080000000-10008009ffff +// 100080350000-10008036ffff +// 100080380000-10008038ffff +// 6fffffe00000-6fffffffffff +// 0x0000000000407af6: __die at libc/log/die.c:36 +// 0x0000000000444f13: __asan_die at libc/intrin/asan.c:318 +// 0x0000000000445bc8: __asan_report at libc/intrin/asan.c:667 +// 0x0000000000445e41: __asan_report_memory_fault at libc/intrin/asan.c:672 +// 0x0000000000446312: __asan_report_store at libc/intrin/asan.c:1008 +// 0x0000000000444442: __asan_report_store1 at libc/intrin/somanyasan.S:118 +// 0x0000000000416216: BssOverrunCrash at test/libc/log/backtrace_test.c:52 +// 0x000000000041642a: SetUp at test/libc/log/backtrace_test.c:73 +// 0x00000000004513eb: testlib_runtestcases at libc/testlib/testrunner.c:98 +// 0x000000000044bbe0: testlib_runalltests at libc/testlib/runner.c:37 +// 0x00000000004026db: main at libc/testlib/testmain.c:155 +// 0x000000000040323f: cosmo at libc/runtime/cosmo.S:64 +// 0x000000000040219b: _start at libc/crt/crt.S:67 +// clang-format on +TEST(ShowCrashReports, testBssOverrunCrash) { + if (!IsAsan()) return; + size_t got; + ssize_t rc; + int ws, pid, fds[2]; + char *output, buf[512]; + ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + dup2(fds[1], 1); + dup2(fds[1], 2); + execv(program_executable_name, + (char *const[]){program_executable_name, "2", 0}); + _exit(127); + } + close(fds[1]); + output = 0; + appends(&output, ""); + for (;;) { + rc = read(fds[0], buf, sizeof(buf)); + if (rc == -1) { + ASSERT_EQ(EINTR, errno); + continue; + } + if ((got = rc)) { + appendd(&output, buf, got); + } else { + break; + } + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(77, WEXITSTATUS(ws)); + /* NULL is stopgap until we can copy symbol tablces into binary */ + if (!OutputHasSymbol(output, "BssOverrunCrash")) { + fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!strstr(output, "☺☻♥♦♣♠•◘○") || !strstr(output, "global redzone")) { + fprintf(stderr, "ERROR: crash report didn't have memory diagram\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + free(output); +} + +// clang-format off +// asan error: null pointer dereference 1-byte load at 0x000000000000 shadow 0x00007fff8000 +// x +// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅∅ +// |-17 |-17 |-17 |-17 |-17 |-1 |-1 |-1 |-1 |-1 +// ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ +// 000000400000-000000464000 .text +// 000000464000-00000046d000 .data +// 00007fff0000-00008000ffff ←shadow +// 000080070000-00008009ffff +// 02008fff0000-02009001ffff +// 020090060000-02009007ffff +// 0e007fff0000-0e008000ffff +// 10000d3e0000-10000d3fffff +// 100080000000-10008009ffff +// 100080370000-10008038ffff +// 1000803a0000-1000803affff +// 6ffffffe0000-6fffffffffff +// 0x0000000000407c84: __die at libc/log/die.c:37 +// 0x000000000040b1ee: __asan_report_load at libc/intrin/asan.c:1083 +// 0x000000000041639e: NpeCrash at test/libc/log/backtrace_test.c:87 +// 0x0000000000416733: SetUp at test/libc/log/backtrace_test.c:120 +// 0x00000000004541fb: testlib_runtestcases at libc/testlib/testrunner.c:98 +// 0x000000000044d000: testlib_runalltests at libc/testlib/runner.c:37 +// 0x00000000004026db: main at libc/testlib/testmain.c:94 +// 0x000000000040327f: cosmo at libc/runtime/cosmo.S:64 +// 0x000000000040219b: _start at libc/crt/crt.S:67 +// clang-format on +TEST(ShowCrashReports, testNpeCrash) { + if (!IsAsan()) return; + size_t got; + ssize_t rc; + int ws, pid, fds[2]; + char *output, buf[512]; + ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + dup2(fds[1], 1); + dup2(fds[1], 2); + execv(program_executable_name, + (char *const[]){program_executable_name, "7", 0}); + _exit(127); + } + close(fds[1]); + output = 0; + appends(&output, ""); + for (;;) { + rc = read(fds[0], buf, sizeof(buf)); + if (rc == -1) { + ASSERT_EQ(EINTR, errno); + continue; + } + if ((got = rc)) { + appendd(&output, buf, got); + } else { + break; + } + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(77, WEXITSTATUS(ws)); + /* NULL is stopgap until we can copy symbol tablces into binary */ + if (!strstr(output, "null pointer dereference")) { + fprintf(stderr, "ERROR: crash report didn't diagnose the problem\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!OutputHasSymbol(output, "NpeCrash")) { + fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!strstr(output, "∅∅∅∅")) { + fprintf(stderr, "ERROR: crash report didn't have shadow diagram\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + free(output); +} + +TEST(ShowCrashReports, testDataOverrunCrash) { + if (!IsAsan()) return; + size_t got; + ssize_t rc; + int ws, pid, fds[2]; + char *output, buf[512]; + ASSERT_NE(-1, pipe2(fds, O_CLOEXEC)); + ASSERT_NE(-1, (pid = vfork())); + if (!pid) { + dup2(fds[1], 1); + dup2(fds[1], 2); + execv(program_executable_name, + (char *const[]){program_executable_name, "4", 0}); + _exit(127); + } + close(fds[1]); + output = 0; + appends(&output, ""); + for (;;) { + rc = read(fds[0], buf, sizeof(buf)); + if (rc == -1) { + ASSERT_EQ(EINTR, errno); + continue; + } + if ((got = rc)) { + appendd(&output, buf, got); + } else { + break; + } + } + ASSERT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(77, WEXITSTATUS(ws)); + /* NULL is stopgap until we can copy symbol tablces into binary */ + if (!OutputHasSymbol(output, "DataOverrunCrash")) { + fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } + if (!strstr(output, "☺☻♥♦♣♠•◘○") || !strstr(output, "global redzone")) { + fprintf(stderr, "ERROR: crash report didn't have memory diagram\n%s\n", + gc(IndentLines(output, -1, 0, 4))); + __die(); + } free(output); } diff --git a/test/libc/log/test.mk b/test/libc/log/test.mk index 284f3d06efc..6a05cf49513 100644 --- a/test/libc/log/test.mk +++ b/test/libc/log/test.mk @@ -29,6 +29,7 @@ TEST_LIBC_LOG_DIRECTDEPS = \ LIBC_STDIO \ LIBC_X \ LIBC_INTRIN \ + LIBC_FMT \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_LOG \ diff --git a/test/libc/mem/arena_test.c b/test/libc/mem/arena_test.c index c1f3debf5bc..3907067e0de 100644 --- a/test/libc/mem/arena_test.c +++ b/test/libc/mem/arena_test.c @@ -16,9 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/libfatal.internal.h" #include "libc/mem/arena.h" #include "libc/mem/mem.h" +#include "libc/stdio/append.internal.h" +#include "libc/str/str.h" #include "libc/testlib/ezbench.h" +#include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" TEST(arena, test) { @@ -37,29 +41,66 @@ TEST(arena, test) { __arena_pop(); } +TEST(arena, testRealloc) { + char *b = 0; + size_t i, n = 0; + __arena_push(); + for (i = 0; i < kHyperionSize; ++i) { + b = realloc(b, ++n * sizeof(*b)); + b[n - 1] = kHyperion[i]; + } + ASSERT_EQ(0, memcmp(b, kHyperion, kHyperionSize)); + __arena_pop(); +} + +void *memalign_(size_t, size_t) asm("memalign"); void *calloc_(size_t, size_t) asm("calloc"); -void A(size_t n) { +void Ca(size_t n) { __arena_push(); for (int i = 0; i < n; ++i) { - calloc_(15, 1); + memalign_(1, 1); } __arena_pop(); } -void B(size_t n) { +void Cb(size_t n) { void **P; P = malloc(n * sizeof(void *)); for (int i = 0; i < n; ++i) { - P[i] = calloc_(15, 1); + P[i] = calloc_(1, 1); } bulk_free(P, n); free(P); } -BENCH(arena, bench) { - EZBENCH2("A 100", donothing, A(100)); - EZBENCH2("B 100", donothing, B(100)); - EZBENCH2("A 5000", donothing, A(5000)); - EZBENCH2("B 5000", donothing, B(5000)); +BENCH(arena, benchMalloc) { + EZBENCH2("arena calloc(1)", donothing, Ca(100)); + EZBENCH2("dlmalloc calloc(1)", donothing, Cb(100)); +} + +void Ra(void) { + long *b = 0; + size_t i, n = 0; + __arena_push(); + for (i = 0; i < kHyperionSize; ++i) { + b = realloc(b, ++n * sizeof(*b)); + b[n - 1] = kHyperion[i]; + } + __arena_pop(); +} + +void Rb(void) { + long *b = 0; + size_t i, n = 0; + for (i = 0; i < kHyperionSize; ++i) { + b = realloc(b, ++n * sizeof(*b)); + b[n - 1] = kHyperion[i]; + } + free(b); +} + +BENCH(arena, benchRealloc) { + EZBENCH2("arena realloc", donothing, Ra()); + EZBENCH2("dlmalloc realloc", donothing, Rb()); } diff --git a/test/libc/mem/malloc_test.c b/test/libc/mem/malloc_test.c index cf87b23f641..b598989f9fb 100644 --- a/test/libc/mem/malloc_test.c +++ b/test/libc/mem/malloc_test.c @@ -23,6 +23,7 @@ #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/rand/rand.h" +#include "libc/runtime/cxaatexit.internal.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" @@ -37,33 +38,49 @@ #define M 20 TEST(malloc, zeroMeansOne) { - ASSERT_GE(malloc_usable_size(malloc(0)), 1); + ASSERT_GE(malloc_usable_size(gc(malloc(0))), 1); } TEST(calloc, zerosMeansOne) { - ASSERT_GE(malloc_usable_size(calloc(0, 0)), 1); + ASSERT_GE(malloc_usable_size(gc(calloc(0, 0))), 1); +} + +void AppendStuff(char **p, size_t *n) { + char buf[512]; + ASSERT_NE(NULL, (*p = realloc(*p, (*n += 512)))); + rngset(buf, sizeof(buf), 0, 0); + memcpy(*p + *n - sizeof(buf), buf, sizeof(buf)); } TEST(malloc, test) { + char *big = 0; + size_t n, bigsize = 0; static struct stat st; static volatile int i, j, k, *A[4096], fds[M], *maps[M], mapsizes[M]; memset(fds, -1, sizeof(fds)); memset(maps, -1, sizeof(maps)); for (i = 0; i < N * M; ++i) { + /* AppendStuff(&big, &bigsize); */ j = rand() % ARRAYLEN(A); if (A[j]) { ASSERT_EQ(j, A[j][0]); - A[j] = realloc(A[j], max(sizeof(int), rand() % N)); + n = rand() % N; + n = MAX(n, 1); + n = ROUNDUP(n, sizeof(int)); + A[j] = realloc(A[j], n); ASSERT_NE(NULL, A[j]); ASSERT_EQ(j, A[j][0]); free(A[j]); A[j] = NULL; } else { - A[j] = malloc(max(sizeof(int), rand() % N)); + n = rand() % N; + n = MAX(n, 1); + n = ROUNDUP(n, sizeof(int)); + A[j] = malloc(n); ASSERT_NE(NULL, A[j]); A[j][0] = j; } - if (i % M == 0) { + if (!(i % M)) { k = rand() % M; if (fds[k] == -1) { ASSERT_NE(-1, (fds[k] = open(program_invocation_name, O_RDONLY))); @@ -78,10 +95,10 @@ TEST(malloc, test) { } } } + free(big); for (i = 0; i < ARRAYLEN(A); ++i) free(A[i]); for (i = 0; i < ARRAYLEN(maps); ++i) munmap(maps[i], mapsizes[i]); for (i = 0; i < ARRAYLEN(fds); ++i) close(fds[i]); - malloc_trim(0); } void *bulk[1024]; diff --git a/test/libc/mem/test.mk b/test/libc/mem/test.mk index 5402d35dff8..037d0de9579 100644 --- a/test/libc/mem/test.mk +++ b/test/libc/mem/test.mk @@ -25,6 +25,7 @@ TEST_LIBC_MEM_DIRECTDEPS = \ LIBC_CALLS \ LIBC_FMT \ LIBC_INTRIN \ + LIBC_LOG \ LIBC_MEM \ LIBC_NEXGEN32E \ LIBC_RAND \ diff --git a/test/libc/nexgen32e/gclongjmp_test.c b/test/libc/nexgen32e/gclongjmp_test.c index c9b572729eb..7d9adb58c3b 100644 --- a/test/libc/nexgen32e/gclongjmp_test.c +++ b/test/libc/nexgen32e/gclongjmp_test.c @@ -27,8 +27,7 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -#undef _gc -#define _gc(x) _defer(Free, x) +#define GC(x) _defer(Free, x) char *x; char *y; @@ -40,20 +39,20 @@ void Free(char *p) { } void C(void) { - x = _gc(strdup("abcd")); - if (0) PrintGarbage(stderr); - _gclongjmp(jb, 1); + x = GC(strdup("abcd")); + if (0) PrintGarbage(); + gclongjmp(jb, 1); abort(); } void B(void C(void)) { - y = _gc(strdup("HIHI")); + y = GC(strdup("HIHI")); C(); abort(); } void A(void C(void), void B(void(void))) { - z = _gc(strdup("yoyo")); + z = GC(strdup("yoyo")); B(C); abort(); } @@ -63,10 +62,12 @@ void (*Bp)(void(void)) = B; void (*Cp)(void) = C; TEST(gclongjmp, test) { + PrintGarbage(); if (!setjmp(jb)) { Ap(Cp, Bp); abort(); } + if (0) PrintGarbage(); EXPECT_STREQ("FREE", x); EXPECT_STREQ("FREE", y); EXPECT_STREQ("FREE", z); @@ -75,12 +76,12 @@ TEST(gclongjmp, test) { free(x); } -void F1(void) { +noinline void F1(void) { /* 3x slower than F2() but sooo worth it */ - _gc(malloc(16)); + gc(malloc(16)); } -void F2(void) { +noinline void F2(void) { void *volatile p; p = malloc(16); free(p); diff --git a/test/libc/release/clang.sh b/test/libc/release/clang.sh index 66a0d514b01..dee70c86463 100755 --- a/test/libc/release/clang.sh +++ b/test/libc/release/clang.sh @@ -2,24 +2,26 @@ #-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐ #───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘ -if CLANG=$(command -v clang); then - mkdir -p o/$MODE/test/libc/release - $CLANG \ - -o o/$MODE/test/libc/release/smokeclang2.com.dbg \ - -Os \ - -Wall \ - -Werror \ - -static \ - -fno-pie \ - -nostdlib \ - -nostdinc \ - -fuse-ld=lld \ - -mno-red-zone \ - -Wl,-T,o/$MODE/ape/ape.lds \ - -include o/cosmopolitan.h \ - test/libc/release/smoke.c \ - o/$MODE/libc/crt/crt.o \ - o/$MODE/ape/ape.o \ - o/$MODE/cosmopolitan.a || exit - o/$MODE/test/libc/release/smokeclang2.com.dbg || exit -fi +# TODO: someone who uses clang please mantain this + +# if CLANG=$(command -v clang); then +# mkdir -p o/$MODE/test/libc/release +# $CLANG \ +# -o o/$MODE/test/libc/release/smokeclang2.com.dbg \ +# -Os \ +# -Wall \ +# -Werror \ +# -static \ +# -fno-pie \ +# -nostdlib \ +# -nostdinc \ +# -fuse-ld=lld \ +# -mno-red-zone \ +# -Wl,-T,o/$MODE/ape/ape.lds \ +# -include o/cosmopolitan.h \ +# test/libc/release/smoke.c \ +# o/$MODE/libc/crt/crt.o \ +# o/$MODE/ape/ape.o \ +# o/$MODE/cosmopolitan.a || exit +# o/$MODE/test/libc/release/smokeclang2.com.dbg || exit +# fi diff --git a/test/libc/release/lld.sh b/test/libc/release/lld.sh index 2e8a7d0d0da..7482ea3c397 100755 --- a/test/libc/release/lld.sh +++ b/test/libc/release/lld.sh @@ -2,26 +2,26 @@ #-*-mode:sh;indent-tabs-mode:nil;tab-width:2;coding:utf-8-*-┐ #───vi: set net ft=sh ts=2 sts=2 fenc=utf-8 :vi─────────────┘ -# TODO(jart): implement me +# TODO: someone who uses clang please mantain this -if CLANG=$(command -v clang); then - mkdir -p o/$MODE/test/libc/release - $CLANG \ - -o o/$MODE/test/libc/release/smokeclang.com.dbg \ - -Os \ - -Wall \ - -Werror \ - -static \ - -fno-pie \ - -nostdlib \ - -nostdinc \ - -fuse-ld=lld \ - -mno-red-zone \ - -Wl,-T,o/$MODE/ape/ape.lds \ - -include o/cosmopolitan.h \ - test/libc/release/smoke.c \ - o/$MODE/libc/crt/crt.o \ - o/$MODE/ape/ape.o \ - o/$MODE/cosmopolitan.a || exit - o/$MODE/test/libc/release/smokeclang.com.dbg || exit -fi +# if CLANG=$(command -v clang); then +# mkdir -p o/$MODE/test/libc/release +# $CLANG \ +# -o o/$MODE/test/libc/release/smokeclang.com.dbg \ +# -Os \ +# -Wall \ +# -Werror \ +# -static \ +# -fno-pie \ +# -nostdlib \ +# -nostdinc \ +# -fuse-ld=lld \ +# -mno-red-zone \ +# -Wl,-T,o/$MODE/ape/ape.lds \ +# -include o/cosmopolitan.h \ +# test/libc/release/smoke.c \ +# o/$MODE/libc/crt/crt.o \ +# o/$MODE/ape/ape.o \ +# o/$MODE/cosmopolitan.a || exit +# o/$MODE/test/libc/release/smokeclang.com.dbg || exit +# fi diff --git a/test/libc/runtime/mremap_test.c b/test/libc/runtime/mremap_test.c new file mode 100644 index 00000000000..95425bd85d5 --- /dev/null +++ b/test/libc/runtime/mremap_test.c @@ -0,0 +1,33 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/testlib/testlib.h" +#include "third_party/dlmalloc/dlmalloc.internal.h" + +TEST(mremap, testMalloc) { + int i; + char *a, *b, *c, *d; + ASSERT_NE(NULL, a = malloc(DEFAULT_MMAP_THRESHOLD)); + ASSERT_NE(NULL, b = mapanon(FRAMESIZE)); + ASSERT_NE(NULL, a = realloc(a, DEFAULT_MMAP_THRESHOLD * 2)); + munmap(b, FRAMESIZE); + free(a); +} diff --git a/test/libc/stdio/dirstream_test.c b/test/libc/stdio/dirstream_test.c index 2e2e9acde3e..f8afc6f0e5b 100644 --- a/test/libc/stdio/dirstream_test.c +++ b/test/libc/stdio/dirstream_test.c @@ -27,6 +27,7 @@ #include "libc/x/x.h" STATIC_YOINK("zip_uri_support"); +STATIC_YOINK("usr/share/zoneinfo/New_York"); TEST(dirstream, test) { DIR *dir; diff --git a/test/libc/stdio/dumphexc_test.c b/test/libc/stdio/dumphexc_test.c index d7bce6d5838..c648f7c998c 100644 --- a/test/libc/stdio/dumphexc_test.c +++ b/test/libc/stdio/dumphexc_test.c @@ -26,7 +26,7 @@ TEST(DumpHexc, test) { \\x68\\x65\\x6c\\x6c\\x6f\\xe2\\x86\\x92\\x0a\\x01\\x02\\x74\\x68\\x65\\x65\\x72\\\n\ \\x68\\x75\\x72\\x63\\x65\\x6f\\x61\\x68\\x72\\x63\\x75\\x6f\\x65\\x61\\x75\\x68\\\n\ \\x63\\x72\"", - DumpHexc("hello→\n\1\2theerhurceoahrcuoeauhcr", -1, 0)); + gc(DumpHexc("hello→\n\1\2theerhurceoahrcuoeauhcr", -1, 0))); } BENCH(DumpHexc, bench) { diff --git a/test/libc/stdio/fgets_test.c b/test/libc/stdio/fgets_test.c index 50ba55dec15..91d658c4d9b 100644 --- a/test/libc/stdio/fgets_test.c +++ b/test/libc/stdio/fgets_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/mem/mem.h" +#include "libc/runtime/gc.internal.h" #include "libc/stdio/stdio.h" #include "libc/testlib/hyperion.h" #include "libc/testlib/testlib.h" @@ -24,7 +25,7 @@ TEST(fgets, test) { FILE *f; char buf[29]; - f = fmemopen(strdup(kHyperion), kHyperionSize, "r+"); + f = fmemopen(gc(strdup(kHyperion)), kHyperionSize, "r+"); ASSERT_STREQ("The fall of Hyperion - a Dre", fgets(buf, sizeof(buf), f)); ASSERT_STREQ("am\n", fgets(buf, sizeof(buf), f)); ASSERT_STREQ("John Keats\n", fgets(buf, sizeof(buf), f)); diff --git a/test/libc/stdio/fmemopen_test.c b/test/libc/stdio/fmemopen_test.c index 19960802ef6..4477ef5fda6 100644 --- a/test/libc/stdio/fmemopen_test.c +++ b/test/libc/stdio/fmemopen_test.c @@ -27,6 +27,7 @@ TEST(fmemopen, testWriteRewindRead) { rewind(f); EXPECT_EQ(1, fread(&c, 1, 1, f)); EXPECT_EQ('c', c); + fclose(f); } /* TEST(fmemopen, testWriteRead_readsNothingButNotEof) { */ diff --git a/test/libc/stdio/getdelim_test.c b/test/libc/stdio/getdelim_test.c index ce5a5779c80..9f37d5cd105 100644 --- a/test/libc/stdio/getdelim_test.c +++ b/test/libc/stdio/getdelim_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/bits/bits.h" +#include "libc/log/libfatal.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/symbols.internal.h" #include "libc/stdio/stdio.h" @@ -95,7 +96,11 @@ void ReadHyperionLines(void) { char *line = NULL; size_t linesize = 0; ASSERT_NE(NULL, (f = fopen("hyperion.txt", "r"))); - while ((rc = getline(&line, &linesize, f)) != -1) { + int i = 0; + for (;;) { + __printf("i=%d\n", i++); + rc = getline(&line, &linesize, f); + if (rc == -1) break; data = xrealloc(data, size + rc); memcpy(data + size, line, rc); size += rc; diff --git a/test/libc/stdio/vappendf_test.c b/test/libc/stdio/vappendf_test.c index 063ab43c4b3..d3475859c50 100644 --- a/test/libc/stdio/vappendf_test.c +++ b/test/libc/stdio/vappendf_test.c @@ -152,7 +152,7 @@ TEST(appendd, testMemFail_doesntFreeExistingAllocation) { char *b = 0; ASSERT_NE(-1, appends(&b, "hello")); EXPECT_STREQ("hello", b); - ASSERT_EQ(-1, appendd(&b, 0, -1ull >> 8)); + ASSERT_EQ(-1, appendd(&b, 0, -1ull >> 7)); EXPECT_STREQ("hello", b); free(b); } diff --git a/test/libc/str/longsort_test.c b/test/libc/str/longsort_test.c index a3b2135284e..3eb8d613192 100644 --- a/test/libc/str/longsort_test.c +++ b/test/libc/str/longsort_test.c @@ -48,4 +48,6 @@ BENCH(longsort, bench) { long *p2 = gc(malloc(n * sizeof(long))); rngset(p1, n * sizeof(long), 0, 0); EZBENCH2("longsort", memcpy(p2, p1, n * sizeof(long)), longsort(p2, n)); + EZBENCH2("qsort", memcpy(p2, p1, n * sizeof(long)), + qsort(p2, n, sizeof(long), CompareLong)); } diff --git a/test/net/http/decodelatin1_test.c b/test/net/http/decodelatin1_test.c index 12fef2decf1..27c395c4380 100644 --- a/test/net/http/decodelatin1_test.c +++ b/test/net/http/decodelatin1_test.c @@ -25,20 +25,20 @@ size_t n; TEST(DecodeLatin1, test) { - EXPECT_STREQ("", DecodeLatin1(NULL, 0, 0)); - EXPECT_STREQ("¥atta", DecodeLatin1("\245atta", -1, &n)); + EXPECT_STREQ("", gc(DecodeLatin1(NULL, 0, 0))); + EXPECT_STREQ("¥atta", gc(DecodeLatin1("\245atta", -1, &n))); EXPECT_EQ(6, n); - EXPECT_STREQ("\245atta", EncodeLatin1("¥atta", -1, &n, 0)); + EXPECT_STREQ("\245atta", gc(EncodeLatin1("¥atta", -1, &n, 0))); EXPECT_EQ(5, n); } TEST(DecodeLatin1, testAbleToImposeCharacterRestrictions) { errno = 0; - EXPECT_EQ(0, EncodeLatin1("\200atta", -1, &n, kControlC1)); + EXPECT_EQ(0, gc(EncodeLatin1("\200atta", -1, &n, kControlC1))); EXPECT_EQ(0, n); EXPECT_EQ(EILSEQ, errno); errno = 0; - EXPECT_EQ(0, EncodeLatin1("\002atta", -1, &n, kControlC0)); + EXPECT_EQ(0, gc(EncodeLatin1("\002atta", -1, &n, kControlC0))); EXPECT_EQ(0, n); EXPECT_EQ(EILSEQ, errno); } @@ -47,22 +47,22 @@ TEST(EncodeLatin1, roundTrip) { int i; char b[256]; for (i = 0; i < 256; ++i) b[i] = i; - char *utf8 = DecodeLatin1(b, 256, &n); + char *utf8 = gc(DecodeLatin1(b, 256, &n)); EXPECT_EQ(384, n); - char *lat1 = EncodeLatin1(utf8, n, &n, 0); + char *lat1 = gc(EncodeLatin1(utf8, n, &n, 0)); ASSERT_EQ(256, n); EXPECT_EQ(0, memcmp(b, lat1, 256)); } TEST(DecodeLatin1, testOom_returnsNullAndSetsSizeToZero) { n = 31337; - EXPECT_EQ(NULL, DecodeLatin1("hello", 0x1000000000000, &n)); + EXPECT_EQ(NULL, gc(DecodeLatin1("hello", 0x10000000000000, &n))); EXPECT_EQ(0, n); } BENCH(DecodeLatin1, bench) { EZBENCH2("DecodeLatin1", donothing, - DecodeLatin1(kHyperion, kHyperionSize, 0)); + free(DecodeLatin1(kHyperion, kHyperionSize, 0))); EZBENCH2("EncodeLatin1", donothing, - EncodeLatin1(kHyperion, kHyperionSize, 0, 0)); + free(EncodeLatin1(kHyperion, kHyperionSize, 0, 0))); } diff --git a/test/net/http/escapehtml_test.c b/test/net/http/escapehtml_test.c index 96928c061fe..ddcf385cbdc 100644 --- a/test/net/http/escapehtml_test.c +++ b/test/net/http/escapehtml_test.c @@ -45,7 +45,7 @@ TEST(escapehtml, testEmpty) { } TEST(escapehtml, testAstralPlanes_doesNothing) { - EXPECT_STREQ("𐌰", escapehtml("𐌰")); + EXPECT_STREQ("𐌰", gc(escapehtml("𐌰"))); } BENCH(escapehtml, bench) { diff --git a/test/net/http/escapejsstringliteral_test.c b/test/net/http/escapejsstringliteral_test.c index 4555bc73374..6704cef6fdf 100644 --- a/test/net/http/escapejsstringliteral_test.c +++ b/test/net/http/escapejsstringliteral_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/runtime/gc.internal.h" #include "libc/stdio/stdio.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/hyperion.h" @@ -32,24 +33,24 @@ char *escapejs(const char *s) { } TEST(EscapeJsStringLiteral, test) { - EXPECT_STREQ("", escapejs("")); - EXPECT_STREQ("\\u00ff", escapejs("\377")); + EXPECT_STREQ("", gc(escapejs(""))); + EXPECT_STREQ("\\u00ff", gc(escapejs("\377"))); EXPECT_STREQ("\\u00ff\\u0080\\u0080\\u0080\\u0080", - escapejs("\377\200\200\200\200")); + gc(escapejs("\377\200\200\200\200"))); EXPECT_STREQ("\\u0001\\u0002\\u0003 \\u0026\\u003d\\u003c\\u003e\\/", - escapejs("\1\2\3 &=<>/")); + gc(escapejs("\1\2\3 &=<>/"))); } TEST(EscapeJsStringLiteral, testUcs2) { - EXPECT_STREQ("\\u00d0\\u263b", escapejs("Ð☻")); + EXPECT_STREQ("\\u00d0\\u263b", gc(escapejs("Ð☻"))); } TEST(EscapeJsStringLiteral, testAstralPlanes) { - EXPECT_STREQ("\\ud800\\udf30\\ud800\\udf30", escapejs("𐌰𐌰")); + EXPECT_STREQ("\\ud800\\udf30\\ud800\\udf30", gc(escapejs("𐌰𐌰"))); } TEST(EscapeJsStringLiteral, testBrokenUnicode_sparesInnocentCharacters) { - EXPECT_STREQ("\\u00e1YO", escapejs("\xE1YO")); + EXPECT_STREQ("\\u00e1YO", gc(escapejs("\xE1YO"))); } void makefile1(void) { diff --git a/test/net/http/escapeurlparam_test.c b/test/net/http/escapeurlparam_test.c index f550279af31..2f2cf5d705e 100644 --- a/test/net/http/escapeurlparam_test.c +++ b/test/net/http/escapeurlparam_test.c @@ -45,7 +45,7 @@ TEST(EscapeParam, testEmpty) { } TEST(EscapeParam, testAstralPlanes_usesUtf8HexEncoding) { - EXPECT_STREQ("%F0%90%8C%B0", escapeparam("𐌰")); + EXPECT_STREQ("%F0%90%8C%B0", gc(escapeparam("𐌰"))); } BENCH(EscapeParam, bench) { diff --git a/test/net/https/mbedtls_test.c b/test/net/https/mbedtls_test.c index 0cc8e3fb7a3..23cd4a068c7 100644 --- a/test/net/https/mbedtls_test.c +++ b/test/net/https/mbedtls_test.c @@ -403,7 +403,7 @@ BENCH(quickjs_remainder, bench) { BENCH(mpi_remainder, bench) { mbedtls_mpi *x, *y, r; - x = str2mpi( + x = gc(str2mpi( "131820409343094310010388979423659136318401916109327276909280345024175692" "811283445510797521231721220331409407564807168230384468176942405812817310" "624525121840385446744443868889563289706427719939300365865529242495144888" @@ -445,8 +445,8 @@ BENCH(mpi_remainder, bench) { "983893514490200568512210790489667188789433092320719785756398772086212370" "409401269127676106581410793787580434036114254547441805771508552049371634" "609025127325512605396392214570059772472666763440181556475095153967113514" - "87546062479444592779055555421362722504575706910949375"); - y = str2mpi( + "87546062479444592779055555421362722504575706910949375")); + y = gc(str2mpi( "402702961953621844286950607555369624422784868935557056881131335461307658" "701727371551406721502307932123276358395008895125652043531209418099658895" "323804953421455502359439932416245276659698167468088937570774479761417692" @@ -471,7 +471,7 @@ BENCH(mpi_remainder, bench) { "674462056105044630573746839553952570745211879290387589347246867522065584" "726369942916093728137773105488374703562705889962546268226061545128021323" "184760695318697037612212579413382773618361971983327301685232523283210570" - "2331094682317528819996876363073536047370469375"); + "2331094682317528819996876363073536047370469375")); mbedtls_mpi_init(&r); EZBENCH2("mpi_remainder", donothing, mbedtls_mpi_mod_mpi(&r, x, y)); mbedtls_mpi_free(&r); @@ -481,7 +481,7 @@ BENCH(mpi_remainder, bench) { BENCH(mpi_mul_int, bench) { mbedtls_mpi *x, y; - x = str2mpi( + x = gc(str2mpi( "131820409343094310010388979423659136318401916109327276909280345024175692" "811283445510797521231721220331409407564807168230384468176942405812817310" "624525121840385446744443868889563289706427719939300365865529242495144888" @@ -523,7 +523,7 @@ BENCH(mpi_mul_int, bench) { "983893514490200568512210790489667188789433092320719785756398772086212370" "409401269127676106581410793787580434036114254547441805771508552049371634" "609025127325512605396392214570059772472666763440181556475095153967113514" - "87546062479444592779055555421362722504575706910949375"); + "87546062479444592779055555421362722504575706910949375")); mbedtls_mpi_init(&y); EZBENCH2("mpi_mul_int", donothing, ({ mbedtls_mpi_copy(&y, x); @@ -560,6 +560,7 @@ TEST(mpi_shift_r, doesntCrash_dontUnderstandWeirdUpstreamBehavior) { TEST(mpi_shift_l, doesntCrash_dontUnderstandWeirdUpstreamBehavior) { mbedtls_mpi x = {1, 0, 0}; EXPECT_EQ(0, mbedtls_mpi_shift_l(&x, 1)); + mbedtls_mpi_free(&x); } TEST(mpi_shift_r, fun0) { @@ -673,7 +674,7 @@ BENCH(gcd, bench) { BENCH(inv_mod, bench3) { mbedtls_mpi g = {0}; - mbedtls_mpi *x = str2mpi16( + mbedtls_mpi *x = gc(str2mpi16( "837B3E23091602B5D14D619D9B2CD79DD039BC9A9F46F0CA1FFD01B398EE42C8EE2142CB" "B295109FC4278DB8AB84A6ADBF319D3297216C349D0EB92925E2794C5FF1AAF664034CB2" "5C15CDA49B7947278AA96BEF9D995C5F99AA4809B12568A1513D8E0A37BB338DC44A1722" @@ -681,8 +682,8 @@ BENCH(inv_mod, bench3) { "7AFFD15FB66B320DF53CEA4D6C9935D0593BFC7A75ABAFDD3016F7C596FA58248BC041CF" "68ED274FA7F7D5BC3E014DDC7BEA4A60DF24805B5F94C998CAF28441FB4A5831755CE935" "2F17F5416647A81A78899E5B2C4D3F6C84A81CEB463C1593392ABCF6BF708A55578EB0EF" - "E9ABF572"); - mbedtls_mpi *y = str2mpi16( + "E9ABF572")); + mbedtls_mpi *y = gc(str2mpi16( "C14DA3DDE7CD1DD104D74972B899AC0E78E43A3C4ACF3A1316D05AE4CDA30088A7EE1E6B" "96A752B490EF2D727A3E249AFCB634AC24F577E026648C9CB0287DA1DAEA8CE6C91C96BC" "FEC10452B336D4A3FAE1B176D890C161B4665236A22653AAAB745E077D1982DB2AD81FA0" @@ -690,7 +691,7 @@ BENCH(inv_mod, bench3) { "C3CD404908EE680CF18B86D246BFD0B8AA11031E7F56A81A1E44180F0F858BDA8B445EE2" "18C6622FC7668DFA5DD87DF327892901C5900E3F27F130C84A0EEFD6DEC7C7276BC7053D" "7AC4023C9A1D3E0FE834985BCB734B5296D811A22C808869395AD30FB0DE592F11C7F7EA" - "12013097"); + "12013097")); mbedtls_mpi_inv_mod(&g, x, y); EZBENCH2("mbedtls_mpi_inv_mod (actual)", donothing, mbedtls_mpi_inv_mod(&g, x, y)); diff --git a/test/tool/build/lib/getargs_test.c b/test/tool/build/lib/getargs_test.c new file mode 100644 index 00000000000..32d9eb173cb --- /dev/null +++ b/test/tool/build/lib/getargs_test.c @@ -0,0 +1,67 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" +#include "tool/build/lib/getargs.h" + +char testlib_enable_tmp_setup_teardown; + +TEST(getargs, test) { + struct GetArgs ga; + char *args[] = {"@a1", "yo", "@a2", "dawg", "@a3", 0}; + ASSERT_SYS(0, 3, creat("a1", 0644)); + ASSERT_SYS(0, 9, write(3, " hi mog\n", 9)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, creat("a2", 0644)); + ASSERT_SYS(0, 1, write(3, "\n", 1)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 3, creat("a3", 0644)); + ASSERT_SYS(0, 4, write(3, "fun\t", 4)); + ASSERT_SYS(0, 0, close(3)); + getargs_init(&ga, args); + EXPECT_STREQ("hi", getargs_next(&ga)); + EXPECT_STREQ("mog", getargs_next(&ga)); + EXPECT_STREQ("yo", getargs_next(&ga)); + EXPECT_STREQ("dawg", getargs_next(&ga)); + EXPECT_STREQ("fun", getargs_next(&ga)); + EXPECT_EQ(NULL, getargs_next(&ga)); + getargs_destroy(&ga); +} + +void GetAllArgs(char **args) { + size_t i = 0; + struct GetArgs ga; + getargs_init(&ga, args); + while (getargs_next(&ga)) ++i; + getargs_destroy(&ga); + ASSERT_EQ(2 + 13790, i); +} + +BENCH(getargs, bench) { + int i; + char *args[] = {"-o", "doge.txt", "@args.txt", 0}; + ASSERT_SYS(0, 3, creat("args.txt", 0644)); + for (i = 0; i < 13790; ++i) + ASSERT_NE(-1, write(3, __argv[0], strlen(__argv[0]) + 1)); + ASSERT_SYS(0, 0, close(3)); + EZBENCH2("getargs", donothing, GetAllArgs(args)); +} diff --git a/test/tool/build/lib/pty_test.c b/test/tool/build/lib/pty_test.c index 2730b335560..21fd838f300 100644 --- a/test/tool/build/lib/pty_test.c +++ b/test/tool/build/lib/pty_test.c @@ -77,7 +77,7 @@ static const char widelatin_golden[] = "\ TEST(pty, testFunWidth) { struct Pty *pty = NewPty(); PtyWrite(pty, widelatin, ARRAYLEN(widelatin) - 1); - EXPECT_STREQ(widelatin_golden, render(pty)); + EXPECT_STREQ(widelatin_golden, gc(render(pty))); FreePty(pty); } @@ -230,6 +230,5 @@ BENCH(pty, bench) { PtyWrite(pty, hyperion, sizeof(hyperion) - 1)); EZBENCH2("pty write kilo", donothing, PtyWrite(pty, kKiloAnsi, sizeof(kKiloAnsi) - 1)); - EZBENCH2("pty render", donothing, render(pty)); FreePty(pty); } diff --git a/third_party/bzip2/bzip2.c b/third_party/bzip2/bzip2.c index 05954eb16b1..f46c7bb50e1 100644 --- a/third_party/bzip2/bzip2.c +++ b/third_party/bzip2/bzip2.c @@ -1,4 +1,5 @@ #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.macros.h" #include "libc/errno.h" @@ -9,6 +10,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/s.h" +#include "libc/sysv/consts/sig.h" #include "libc/time/struct/utimbuf.h" #include "libc/time/time.h" #include "third_party/bzip2/bzlib.h" diff --git a/third_party/chibicc/alloc.c b/third_party/chibicc/alloc.c index 9f5cf9a6364..c33919a7c18 100644 --- a/third_party/chibicc/alloc.c +++ b/third_party/chibicc/alloc.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" #include "third_party/chibicc/chibicc.h" long alloc_node_count; @@ -23,22 +25,33 @@ long alloc_token_count; long alloc_obj_count; long alloc_type_count; +wontreturn void __oom_hook(size_t request) { + fprintf(stderr, "error: chibicc ran out of memory\n"); + exit(1); +} + +static void *alloc(size_t n, long *c) { + void *r; + if ((r = calloc(1, n))) { + ++*c; + return r; + } else { + __oom_hook(n); + } +} + Node *alloc_node(void) { - ++alloc_node_count; - return calloc(1, sizeof(Node)); + return alloc(sizeof(Node), &alloc_node_count); } Token *alloc_token(void) { - ++alloc_token_count; - return calloc(1, sizeof(Token)); + return alloc(sizeof(Token), &alloc_token_count); } Obj *alloc_obj(void) { - ++alloc_obj_count; - return calloc(1, sizeof(Obj)); + return alloc(sizeof(Obj), &alloc_obj_count); } Type *alloc_type(void) { - ++alloc_type_count; - return calloc(1, sizeof(Type)); + return alloc(sizeof(Type), &alloc_type_count); } diff --git a/third_party/chibicc/chibicc.c b/third_party/chibicc/chibicc.c index 9b64c5ecca5..16e09c9a6f0 100644 --- a/third_party/chibicc/chibicc.c +++ b/third_party/chibicc/chibicc.c @@ -66,20 +66,31 @@ static char *output_file; static StringArray input_paths; char **chibicc_tmpfiles; -static void usage(int status) { +static const char kChibiccVersion[] = "\ +chibicc (cosmopolitan) 9.0.0\n\ +copyright 2019 rui ueyama\n\ +copyright 2020 justine alexandra roberts tunney\n"; + +static void chibicc_version(void) { + xwrite(1, kChibiccVersion, sizeof(kChibiccVersion) - 1); + _Exit(0); +} + +static void chibicc_usage(int status) { char *p; size_t n; - p = gc(xslurp("/zip/third_party/chibicc/help.txt", &n)); + p = xslurp("/zip/third_party/chibicc/help.txt", &n); xwrite(1, p, n); - exit(status); + _Exit(status); } -static void version(void) { - printf("\ -chibicc (cosmopolitan) 9.0.0\n\ -copyright 2019 rui ueyama\n\ -copyright 2020 justine alexandra roberts tunney\n"); - exit(0); +void chibicc_cleanup(void) { + size_t i; + if (chibicc_tmpfiles && !opt_save_temps) { + for (i = 0; chibicc_tmpfiles[i]; i++) { + unlink(chibicc_tmpfiles[i]); + } + } } static bool take_arg(char *arg) { @@ -185,7 +196,7 @@ static void parse_args(int argc, char **argv) { for (int i = 1; i < argc; i++) { if (take_arg(argv[i])) { if (!argv[++i]) { - usage(1); + chibicc_usage(1); } } } @@ -196,9 +207,9 @@ static void parse_args(int argc, char **argv) { } else if (!strcmp(argv[i], "-cc1")) { opt_cc1 = true; } else if (!strcmp(argv[i], "--help")) { - usage(0); + chibicc_usage(0); } else if (!strcmp(argv[i], "--version")) { - version(); + chibicc_version(); } else if (!strcmp(argv[i], "-v")) { opt_verbose = true; atexit(PrintMemoryUsage); @@ -365,15 +376,6 @@ static char *replace_extn(char *tmpl, char *extn) { return buf; } -static void cleanup(void) { - size_t i; - if (chibicc_tmpfiles && !opt_save_temps) { - for (i = 0; chibicc_tmpfiles[i]; i++) { - unlink(chibicc_tmpfiles[i]); - } - } -} - static char *create_tmpfile(void) { char *path = xjoinpaths(kTmpPath, "chibicc-XXXXXX"); int fd = mkstemp(path); @@ -442,7 +444,7 @@ static bool run_subprocess(char **argv) { if (!vfork()) { // Child process. Run a new command. execvp(argv[0], argv); - _exit(1); + _Exit(1); } // Wait for the child process to finish. do rc = wait(&ws); @@ -683,9 +685,7 @@ static void OnCtrlC(int sig, siginfo_t *si, ucontext_t *ctx) { } int chibicc(int argc, char **argv) { - showcrashreports(); sigaction(SIGINT, &(struct sigaction){.sa_sigaction = OnCtrlC}, NULL); - atexit(cleanup); for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "-cc1")) { opt_cc1 = true; diff --git a/third_party/chibicc/chibicc.h b/third_party/chibicc/chibicc.h index 193acdc567f..9d9d35232ee 100644 --- a/third_party/chibicc/chibicc.h +++ b/third_party/chibicc/chibicc.h @@ -608,6 +608,7 @@ extern char *base_file; extern char **chibicc_tmpfiles; int chibicc(int, char **); +void chibicc_cleanup(void); // // alloc.c diff --git a/third_party/chibicc/chibicc.main.c b/third_party/chibicc/chibicc.main.c index 05a2b009942..6c218fcb63d 100644 --- a/third_party/chibicc/chibicc.main.c +++ b/third_party/chibicc/chibicc.main.c @@ -1,3 +1,5 @@ +#include "libc/mem/arena.h" +#include "libc/runtime/internal.h" #include "third_party/chibicc/chibicc.h" int main(int argc, char **argv) { diff --git a/third_party/chibicc/test/test.mk b/third_party/chibicc/test/test.mk index 687e9a9a581..61824fda495 100644 --- a/third_party/chibicc/test/test.mk +++ b/third_party/chibicc/test/test.mk @@ -93,8 +93,10 @@ o/$(MODE)/third_party/chibicc/test/%2.com.dbg: \ .PRECIOUS: $(THIRD_PARTY_CHIBICC_TEST_OBJS) .PRECIOUS: $(THIRD_PARTY_CHIBICC_TEST2_OBJS) -o/$(MODE)/third_party/chibicc/test/int128_test.chibicc.o: QUOTA = -M512m -o/$(MODE)/third_party/chibicc/test/int128_test.chibicc2.o: QUOTA = -M512m +o/$(MODE)/third_party/chibicc/test/int128_test.o: QUOTA = -M512m +o/$(MODE)/third_party/chibicc/test/int128_test.o: QUOTA = -M512m +o/$(MODE)/third_party/chibicc/test/int128_test.chibicc.o: QUOTA = -M1024m +o/$(MODE)/third_party/chibicc/test/int128_test.chibicc2.o: QUOTA = -M1024m .PHONY: o/$(MODE)/third_party/chibicc/test o/$(MODE)/third_party/chibicc/test: \ diff --git a/third_party/dlmalloc/README b/third_party/dlmalloc/README index 7e9ced0c99e..6c542bd1a98 100644 --- a/third_party/dlmalloc/README +++ b/third_party/dlmalloc/README @@ -389,10 +389,6 @@ HAVE_MMAP default: 1 (true) able to unmap memory that may have be allocated using multiple calls to MMAP, so long as they are adjacent. -HAVE_MREMAP default: 1 on linux, else 0 - If true realloc() uses mremap() to re-allocate large blocks and - extend or shrink allocation spaces. - MMAP_CLEARS default: 1 except on WINCE. True if mmap clears memory so calloc doesn't need to. This is true for standard unix mmap using /dev/zero and on WIN32 except for WINCE. diff --git a/third_party/dlmalloc/bulk_free.c b/third_party/dlmalloc/bulk_free.c index 77d4c285cb5..54fa98926a1 100644 --- a/third_party/dlmalloc/bulk_free.c +++ b/third_party/dlmalloc/bulk_free.c @@ -10,6 +10,9 @@ * to sort this array before calling bulk_free. */ size_t dlbulk_free(void *array[], size_t nelem) { + void **a, **b, *mem, **fence; + struct MallocChunk *p, *next; + size_t psize, newsize, unfreed; /* * Try to free all pointers in the given array. Note: this could be * made faster, by delaying consolidation, at the price of disabling @@ -17,15 +20,15 @@ size_t dlbulk_free(void *array[], size_t nelem) { * by combining adjacent chunks before freeing, which will occur often * if allocated with ialloc or the array is sorted. */ - size_t unfreed = 0; + unfreed = 0; if (!PREACTION(g_dlmalloc)) { - void **a; - void **fence = &(array[nelem]); + a; + fence = &(array[nelem]); for (a = array; a != fence; ++a) { - void *mem = *a; + mem = *a; if (mem != 0) { - mchunkptr p = mem2chunk(AddressDeathAction(mem)); - size_t psize = chunksize(p); + p = mem2chunk(AddressDeathAction(mem)); + psize = chunksize(p); #if FOOTERS if (get_mstate_for(p) != g_dlmalloc) { ++unfreed; @@ -35,10 +38,10 @@ size_t dlbulk_free(void *array[], size_t nelem) { check_inuse_chunk(g_dlmalloc, p); *a = 0; if (RTCHECK(ok_address(g_dlmalloc, p) && ok_inuse(p))) { - void **b = a + 1; /* try to merge with next chunk */ - mchunkptr next = next_chunk(p); + b = a + 1; /* try to merge with next chunk */ + next = next_chunk(p); if (b != fence && *b == chunk2mem(next)) { - size_t newsize = chunksize(next) + psize; + newsize = chunksize(next) + psize; set_inuse(g_dlmalloc, p, newsize); *b = chunk2mem(p); } else diff --git a/third_party/dlmalloc/dlcalloc.c b/third_party/dlmalloc/dlcalloc.c index 9e132ce717a..ea84df9940d 100644 --- a/third_party/dlmalloc/dlcalloc.c +++ b/third_party/dlmalloc/dlcalloc.c @@ -6,7 +6,7 @@ void *dlcalloc(size_t n_elements, size_t elem_size) { size_t req; if (__builtin_mul_overflow(n_elements, elem_size, &req)) req = -1; mem = dlmalloc(req); - if (mem != 0 && calloc_must_clear(mem2chunk(mem))) { + if (mem && calloc_must_clear(mem2chunk(mem))) { bzero(mem, req); } return mem; diff --git a/third_party/dlmalloc/dlindependent_calloc.c b/third_party/dlmalloc/dlindependent_calloc.c index 499f63d1b3f..a847335b08a 100644 --- a/third_party/dlmalloc/dlindependent_calloc.c +++ b/third_party/dlmalloc/dlindependent_calloc.c @@ -96,7 +96,7 @@ static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, } } -#if DEBUG + MODE_DBG + 0 +#ifdef DEBUG if (marray != chunks) { /* final element must have exactly exhausted chunk */ if (element_size != 0) { @@ -106,9 +106,10 @@ static void **ialloc(mstate m, size_t n_elements, size_t *sizes, int opts, } check_inuse_chunk(m, mem2chunk(marray)); } - for (i = 0; i != n_elements; ++i) check_inuse_chunk(m, mem2chunk(marray[i])); - -#endif /* DEBUG */ + for (i = 0; i != n_elements; ++i) { + check_inuse_chunk(m, mem2chunk(marray[i])); + } +#endif /* IsModeDbg() */ POSTACTION(m); return marray; diff --git a/third_party/dlmalloc/dlmalloc.c b/third_party/dlmalloc/dlmalloc.c index d08e1fca809..302967311ea 100644 --- a/third_party/dlmalloc/dlmalloc.c +++ b/third_party/dlmalloc/dlmalloc.c @@ -1,3 +1,4 @@ +#include "libc/assert.h" #include "libc/bits/initializer.internal.h" #include "libc/bits/safemacros.internal.h" #include "libc/bits/weaken.h" @@ -13,6 +14,7 @@ #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nt/systeminfo.h" +#include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" @@ -51,7 +53,8 @@ static void *dlmalloc_requires_more_vespene_gas(size_t size) { /* ─────────────────────────── mspace management ─────────────────────────── */ /* Initialize top chunk and its size */ -static void dlmalloc_init_top(mstate m, mchunkptr p, size_t psize) { +static void dlmalloc_init_top(struct MallocState *m, mchunkptr p, + size_t psize) { /* Ensure alignment */ size_t offset = align_offset(chunk2mem(p)); p = (mchunkptr)((char *)p + offset); @@ -65,7 +68,7 @@ static void dlmalloc_init_top(mstate m, mchunkptr p, size_t psize) { } /* Initialize bins for a new mstate that is otherwise zeroed out */ -static void init_bins(mstate m) { +static void init_bins(struct MallocState *m) { /* Establish circular links for smallbins */ bindex_t i; for (i = 0; i < NSMALLBINS; ++i) { @@ -75,8 +78,8 @@ static void init_bins(mstate m) { } /* Allocate chunk and prepend remainder with chunk in successor base. */ -static void *dlmalloc_prepend_alloc(mstate m, char *newbase, char *oldbase, - size_t nb) { +static void *dlmalloc_prepend_alloc(struct MallocState *m, char *newbase, + char *oldbase, size_t nb) { mchunkptr p = align_as_chunk(newbase); mchunkptr oldfirst = align_as_chunk(oldbase); size_t psize = (char *)oldfirst - (char *)p; @@ -112,13 +115,13 @@ static void *dlmalloc_prepend_alloc(mstate m, char *newbase, char *oldbase, } /* Add a segment to hold a new noncontiguous region */ -static void dlmalloc_add_segment(mstate m, char *tbase, size_t tsize, - flag_t mmapped) { +static void dlmalloc_add_segment(struct MallocState *m, char *tbase, + size_t tsize, flag_t mmapped) { /* Determine locations and sizes of segment, fenceposts, old top */ char *old_top = (char *)m->top; msegmentptr oldsp = segment_holding(m, old_top); char *old_end = oldsp->base + oldsp->size; - size_t ssize = pad_request(sizeof(struct malloc_segment)); + size_t ssize = pad_request(sizeof(struct MallocSegment)); char *rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); size_t offset = align_offset(chunk2mem(rawsp)); char *asp = rawsp + offset; @@ -163,8 +166,10 @@ static void dlmalloc_add_segment(mstate m, char *tbase, size_t tsize, /* ─────────────────────────── system integration ─────────────────────────── */ /* Return true if segment contains a segment link */ -static int has_segment_link(mstate m, msegmentptr ss) { - msegmentptr sp = &m->seg; +noinline int has_segment_link(struct MallocState *m, msegmentptr ss) { + msegmentptr sp; + assert(m); + sp = &m->seg; for (;;) { if ((char *)sp >= ss->base && (char *)sp < ss->base + ss->size) return 1; if ((sp = sp->next) == 0) return 0; @@ -183,7 +188,7 @@ static int has_segment_link(mstate m, msegmentptr ss) { #define SYS_ALLOC_PADDING (TOP_FOOT_SIZE + MALLOC_ALIGNMENT) /* Malloc using mmap */ -static void *mmap_alloc(mstate m, size_t nb) { +static void *mmap_alloc(struct MallocState *m, size_t nb) { size_t mmsize = mmap_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); if (m->footprint_limit != 0) { size_t fp = m->footprint + mmsize; @@ -214,7 +219,7 @@ static void *mmap_alloc(mstate m, size_t nb) { /** * Gets memory from system. */ -static void *dlmalloc_sys_alloc(mstate m, size_t nb) { +static void *dlmalloc_sys_alloc(struct MallocState *m, size_t nb) { char *tbase = CMFAIL; size_t tsize = 0; flag_t mmap_flag = 0; @@ -310,7 +315,7 @@ static void *dlmalloc_sys_alloc(mstate m, size_t nb) { } /* Unmap and unlink any mmapped segments that don't contain used chunks */ -static size_t dlmalloc_release_unused_segments(mstate m) { +static size_t dlmalloc_release_unused_segments(struct MallocState *m) { size_t released = 0; int nsegs = 0; msegmentptr pred = &m->seg; @@ -357,7 +362,7 @@ static size_t dlmalloc_release_unused_segments(mstate m) { return released; } -int dlmalloc_sys_trim(mstate m, size_t pad) { +int dlmalloc_sys_trim(struct MallocState *m, size_t pad) { size_t released = 0; ensure_initialization(); if (pad < MAX_REQUEST && is_initialized(m)) { @@ -416,7 +421,7 @@ static void post_fork_child(void) { /* Consolidate and bin a chunk. Differs from exported versions of free mainly in that the chunk need not be marked as inuse. */ -void dlmalloc_dispose_chunk(mstate m, mchunkptr p, size_t psize) { +void dlmalloc_dispose_chunk(struct MallocState *m, mchunkptr p, size_t psize) { mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { mchunkptr prev; @@ -480,7 +485,7 @@ void dlmalloc_dispose_chunk(mstate m, mchunkptr p, size_t psize) { /* ──────────────────────────── malloc ─────────────────────────── */ /* allocate a small request from the best fitting chunk in a treebin */ -static void *tmalloc_small(mstate m, size_t nb) { +static void *tmalloc_small(struct MallocState *m, size_t nb) { tchunkptr t, v; size_t rsize; bindex_t i; @@ -515,7 +520,7 @@ static void *tmalloc_small(mstate m, size_t nb) { } /* allocate a large request from the best fitting chunk in a treebin */ -static void *tmalloc_large(mstate m, size_t nb) { +static void *tmalloc_large(struct MallocState *m, size_t nb) { tchunkptr v = 0; size_t rsize = -nb; /* Unsigned negation */ tchunkptr t; @@ -717,11 +722,8 @@ void *dlmalloc_impl(size_t bytes, bool takeaction) { return 0; } -void *dlmalloc(size_t bytes) { - return dlmalloc_impl(bytes, true); -} - void dlfree(void *mem) { + /* asan runtime depends on this function */ /* Consolidate freed chunks with preceeding or succeeding bordering free chunks, if they exist, and then place in a bin. Intermixed @@ -732,7 +734,7 @@ void dlfree(void *mem) { mchunkptr p = mem2chunk(mem); #if FOOTERS - mstate fm = get_mstate_for(p); + struct MallocState *fm = get_mstate_for(p); if (!ok_magic(fm)) { /* HELLO * TRY #1: rm -rf o && make -j8 -O MODE=dbg * TRY #2: gdb: p/x (long*)(p+(*(long*)(p-8)&~(1|2|3))) @@ -784,7 +786,9 @@ void dlfree(void *mem) { fm->dv = 0; fm->dvsize = 0; } - if (should_trim(fm, tsize)) dlmalloc_sys_trim(fm, 0); + if (should_trim(fm, tsize)) { + dlmalloc_sys_trim(fm, 0); + } goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; @@ -818,6 +822,7 @@ void dlfree(void *mem) { } } erroraction: + if (IsArenaFrame((intptr_t)p >> 16)) return; USAGE_ERROR_ACTION(fm, p); postaction: POSTACTION(fm); @@ -829,6 +834,7 @@ void dlfree(void *mem) { } size_t dlmalloc_usable_size(const void *mem) { + /* asan runtime depends on this function */ if (mem != 0) { mchunkptr p = mem2chunk(mem); if (is_inuse(p)) return chunksize(p) - overhead_for(p); @@ -879,22 +885,19 @@ textstartup void dlmalloc_init(void) { RELEASE_MALLOC_GLOBAL_LOCK(); } -void *dlmemalign$impl(mstate m, size_t alignment, size_t bytes) { - void *mem = 0; - if (bytes >= MAX_REQUEST - alignment) { - if (m != 0) { /* Test isn't needed but avoids compiler warning */ - enomem(); - } - } else { +void *dlmemalign_impl(struct MallocState *m, size_t al, size_t bytes) { + char *br, *pos, *mem = 0; + mchunkptr p, newp, remainder; + size_t nb, req, size, leadsize, newsize, remainder_size; + if (bytes < MAX_REQUEST - al) { /* alignment is 32+ bytes rounded up to nearest two power */ - alignment = 2ul << bsrl(MAX(MIN_CHUNK_SIZE, alignment) - 1); - size_t nb = request2size(bytes); - size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; - mem = dlmalloc_impl(req, false); - if (mem != 0) { - mchunkptr p = mem2chunk(mem); + al = 2ul << bsrl(MAX(MIN_CHUNK_SIZE, al) - 1); + nb = request2size(bytes); + req = nb + al + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + if ((mem = dlmalloc_impl(req, false))) { + p = mem2chunk(mem); if (PREACTION(m)) return 0; - if ((((size_t)(mem)) & (alignment - 1)) != 0) { /* misaligned */ + if ((((size_t)(mem)) & (al - 1))) { /* misaligned */ /* Find an aligned spot inside chunk. Since we need to give back leading space in a chunk of at least MIN_CHUNK_SIZE, if @@ -903,14 +906,11 @@ void *dlmemalign$impl(mstate m, size_t alignment, size_t bytes) { We've allocated enough total room so that this is always possible. */ - char *br = (char *)mem2chunk((size_t)( - ((size_t)((char *)mem + alignment - SIZE_T_ONE)) & -alignment)); - char *pos = ((size_t)(br - (char *)(p)) >= MIN_CHUNK_SIZE) - ? br - : br + alignment; - mchunkptr newp = (mchunkptr)pos; - size_t leadsize = pos - (char *)(p); - size_t newsize = chunksize(p) - leadsize; + br = (char *)mem2chunk(ROUNDUP((uintptr_t)mem, al)); + pos = (size_t)(br - (char *)(p)) >= MIN_CHUNK_SIZE ? br : br + al; + newp = (mchunkptr)pos; + leadsize = pos - (char *)(p); + newsize = chunksize(p) - leadsize; if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ newp->prev_foot = p->prev_foot + leadsize; newp->head = newsize; @@ -923,10 +923,10 @@ void *dlmemalign$impl(mstate m, size_t alignment, size_t bytes) { } /* Give back spare room at the end */ if (!is_mmapped(p)) { - size_t size = chunksize(p); + size = chunksize(p); if (size > nb + MIN_CHUNK_SIZE) { - size_t remainder_size = size - nb; - mchunkptr remainder = chunk_plus_offset(p, nb); + remainder_size = size - nb; + remainder = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, remainder, remainder_size); dlmalloc_dispose_chunk(m, remainder, remainder_size); @@ -934,15 +934,26 @@ void *dlmemalign$impl(mstate m, size_t alignment, size_t bytes) { } mem = chunk2mem(p); assert(chunksize(p) >= nb); - assert(((size_t)mem & (alignment - 1)) == 0); + assert(!((size_t)mem & (al - 1))); check_inuse_chunk(m, p); POSTACTION(m); } + return AddressBirthAction(mem); + } else { + enomem(); + return 0; } - return AddressBirthAction(mem); +} + +void *dlmalloc(size_t bytes) { + return dlmalloc_impl(bytes, true); } void *dlmemalign(size_t alignment, size_t bytes) { - if (alignment <= MALLOC_ALIGNMENT) return dlmalloc(bytes); - return dlmemalign$impl(g_dlmalloc, alignment, bytes); + /* asan runtime depends on this function */ + if (alignment <= MALLOC_ALIGNMENT) { + return dlmalloc_impl(bytes, true); + } else { + return dlmemalign_impl(g_dlmalloc, alignment, bytes); + } } diff --git a/third_party/dlmalloc/dlmalloc.internal.h b/third_party/dlmalloc/dlmalloc.internal.h index a2afa9c1aeb..1c8bdd67a28 100644 --- a/third_party/dlmalloc/dlmalloc.internal.h +++ b/third_party/dlmalloc/dlmalloc.internal.h @@ -18,19 +18,15 @@ COSMOPOLITAN_C_START_ */ #endif -#define DLMALLOC_VERSION 20806 - -#ifndef FOOTERS -#define FOOTERS !NoDebug() -#endif - +#define DLMALLOC_VERSION 20806 #define HAVE_MMAP 1 -#define HAVE_MREMAP 0 /* IsLinux() */ #define MMAP_CLEARS 1 -#define MALLOC_ALIGNMENT __BIGGEST_ALIGNMENT__ +#define MALLOC_ALIGNMENT 16 #define NO_SEGMENT_TRAVERSAL 1 #define MAX_RELEASE_CHECK_RATE 128 #define MALLOC_ABORT abort() +#define FOOTERS !NoDebug() +#define MAX_REQUEST 0xfffffffffff #define DEFAULT_GRANULARITY (64UL * 1024UL) #define DEFAULT_TRIM_THRESHOLD (10UL * 1024UL * 1024UL) #define DEFAULT_MMAP_THRESHOLD (256UL * 1024UL) @@ -137,7 +133,7 @@ COSMOPOLITAN_C_START_ /* (The following includes lightly edited explanations by Colin Plumb.) - The malloc_chunk declaration below is misleading (but accurate and + The MallocChunk declaration below is misleading (but accurate and necessary). It declares a "view" into memory allowing access to necessary fields at known offsets from a given base. @@ -269,19 +265,19 @@ COSMOPOLITAN_C_START_ */ -struct malloc_chunk { - size_t prev_foot; /* Size of previous chunk (if free). */ - size_t head; /* Size and inuse bits. */ - struct malloc_chunk *fd; /* double links -- used only if free. */ - struct malloc_chunk *bk; +struct MallocChunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct MallocChunk *fd; /* double links -- used only if free. */ + struct MallocChunk *bk; }; -typedef struct malloc_chunk mchunk; -typedef struct malloc_chunk *mchunkptr; -typedef struct malloc_chunk *sbinptr; /* The type of bins of chunks */ -typedef unsigned int bindex_t; /* Described below */ -typedef unsigned int binmap_t; /* Described below */ -typedef unsigned int flag_t; /* The type of various bit flag sets */ +typedef struct MallocChunk mchunk; +typedef struct MallocChunk *mchunkptr; +typedef struct MallocChunk *sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ /* ─────────────────── Chunks sizes and alignments ─────────────────────── */ @@ -304,7 +300,6 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ #define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) /* Bounds on request (not chunk) sizes. */ -#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) #define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) /* pad request bytes into a usable size */ @@ -351,7 +346,7 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ #define chunk_plus_offset(p, s) ((mchunkptr)(((char *)(p)) + (s))) #define chunk_minus_offset(p, s) ((mchunkptr)(((char *)(p)) - (s))) -/* Ptr to next or previous physical malloc_chunk. */ +/* Ptr to next or previous physical MallocChunk. */ #define next_chunk(p) ((mchunkptr)(((char *)(p)) + ((p)->head & ~FLAG_BITS))) #define prev_chunk(p) ((mchunkptr)(((char *)(p)) - ((p)->prev_foot))) @@ -403,7 +398,7 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ └───────────────────────────────────────────────────────────────┘ Larger chunks are kept in a form of bitwise digital trees (aka - tries) keyed on chunksizes. Because malloc_tree_chunks are only for + tries) keyed on chunksizes. Because MallocTreeChunks are only for free chunks greater than 256 bytes, their size doesn't impose any constraints on user chunk sizes. Each node looks like: @@ -468,21 +463,20 @@ typedef unsigned int flag_t; /* The type of various bit flag sets */ is of course much better. */ -struct malloc_tree_chunk { - /* The first four fields must be compatible with malloc_chunk */ +struct MallocTreeChunk { + /* The first four fields must be compatible with MallocChunk */ size_t prev_foot; size_t head; - struct malloc_tree_chunk *fd; - struct malloc_tree_chunk *bk; - - struct malloc_tree_chunk *child[2]; - struct malloc_tree_chunk *parent; + struct MallocTreeChunk *fd; + struct MallocTreeChunk *bk; + struct MallocTreeChunk *child[2]; + struct MallocTreeChunk *parent; bindex_t index; }; -typedef struct malloc_tree_chunk tchunk; -typedef struct malloc_tree_chunk *tchunkptr; -typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ +typedef struct MallocTreeChunk tchunk; +typedef struct MallocTreeChunk *tchunkptr; +typedef struct MallocTreeChunk *tbinptr; /* The type of bins of trees */ /* A little helper macro for trees */ #define leftmost_child(t) ((t)->child[0] != 0 ? (t)->child[0] : (t)->child[1]) @@ -490,46 +484,44 @@ typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ /* ───────────────────────────── Segments ──────────────────────────────── */ /* - Each malloc space may include non-contiguous segments, held in a - list headed by an embedded malloc_segment record representing the - top-most space. Segments also include flags holding properties of - the space. Large chunks that are directly allocated by mmap are not - included in this list. They are instead independently created and - destroyed without otherwise keeping track of them. + Each malloc space may include non-contiguous segments, held in a list + headed by an embedded MallocSegment record representing the top-most + space. Segments also include flags holding properties of the space. + Large chunks that are directly allocated by mmap are not included in + this list. They are instead independently created and destroyed + without otherwise keeping track of them. Segment management mainly comes into play for spaces allocated by - MMAP. Any call to MMAP might or might not return memory that is - adjacent to an existing segment. MORECORE normally contiguously + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously extends the current space, so this space is almost always adjacent, which is simpler and faster to deal with. (This is why MORECORE is - used preferentially to MMAP when both are available -- see - sys_alloc.) When allocating using MMAP, we don't use any of the - hinting mechanisms (inconsistently) supported in various - implementations of unix mmap, or distinguish reserving from - committing memory. Instead, we just ask for space, and exploit - contiguity when we get it. It is probably possible to do - better than this on some systems, but no general scheme seems - to be significantly better. - - Management entails a simpler variant of the consolidation scheme - used for chunks to reduce fragmentation -- new adjacent memory is - normally prepended or appended to an existing segment. However, - there are limitations compared to chunk consolidation that mostly - reflect the fact that segment processing is relatively infrequent - (occurring only when getting memory from system) and that we - don't expect to have huge numbers of segments: - - * Segments are not indexed, so traversal requires linear scans. (It + used preferentially to MMAP when both are available -- see sys_alloc.) + When allocating using MMAP, we don't use any of the hinting mechanisms + (inconsistently) supported in various implementations of unix mmap, or + distinguish reserving from committing memory. Instead, we just ask for + space, and exploit contiguity when we get it. It is probably possible + to do better than this on some systems, but no general scheme seems to + be significantly better. + + Management entails a simpler variant of the consolidation scheme used + for chunks to reduce fragmentation -- new adjacent memory is normally + prepended or appended to an existing segment. However, there are + limitations compared to chunk consolidation that mostly reflect the + fact that segment processing is relatively infrequent (occurring only + when getting memory from system) and that we don't expect to have huge + numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It would be possible to index these, but is not worth the extra overhead and complexity for most programs on most platforms.) * New segments are only appended to old ones when holding top-most memory; if they cannot be prepended to others, they are held in different segments. - Except for the top-most segment of an mstate, each segment record - is kept at the tail of its segment. Segments are added by pushing - segment records onto the list headed by &mstate.seg for the - containing mstate. + Except for the top-most segment of an mstate, each segment record is + kept at the tail of its segment. Segments are added by pushing segment + records onto the list headed by &mstate.seg for the containing mstate. Segment flags control allocation/merge/deallocation policies: * If EXTERN_BIT set, then we did not allocate this segment, @@ -544,18 +536,18 @@ typedef struct malloc_tree_chunk *tbinptr; /* The type of bins of trees */ and deallocated/trimmed using MORECORE with negative arguments. */ -struct malloc_segment { - char *base; /* base address */ - size_t size; /* allocated size */ - struct malloc_segment *next; /* ptr to next segment */ - flag_t sflags; /* mmap and extern flag */ +struct MallocSegment { + char *base; /* base address */ + size_t size; /* allocated size */ + struct MallocSegment *next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ }; #define is_mmapped_segment(S) ((S)->sflags & USE_MMAP_BIT) #define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) -typedef struct malloc_segment msegment; -typedef struct malloc_segment *msegmentptr; +typedef struct MallocSegment msegment; +typedef struct MallocSegment *msegmentptr; /* ──────────────────────────── MallocState ───────────────────────────── */ @@ -583,7 +575,7 @@ typedef struct malloc_segment *msegmentptr; An array of bin headers for free chunks. These bins hold chunks with sizes less than MIN_LARGE_SIZE bytes. Each bin contains chunks of all the same size, spaced 8 bytes apart. To simplify - use in double-linked lists, each bin header acts as a malloc_chunk + use in double-linked lists, each bin header acts as a MallocChunk pointing to the real first node, if it exists (else pointing to itself). This avoids special-casing for headers. But to avoid waste, we allocate only the fd/bk pointers of bins, and then use @@ -609,7 +601,7 @@ typedef struct malloc_segment *msegmentptr; well as to reduce the number of memory locations read or written. Segments - A list of segments headed by an embedded malloc_segment record + A list of segments headed by an embedded MallocSegment record representing the initial space. Address check support @@ -715,10 +707,12 @@ for k,v in d.items(): */ #define MALLOC_TRACE 0 -static inline void *AddressBirthAction(void *p) { +forceinline void *AddressBirthAction(void *p) { #if MALLOC_TRACE (dprintf)(2, "BIRTH %p\n", p); - if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) { + if (weaken(ShowBacktrace)) { + weaken(ShowBacktrace)(2, 0); + } else if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) { weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0), weaken(GetSymbolTable)()); } @@ -726,10 +720,12 @@ static inline void *AddressBirthAction(void *p) { return p; } -static inline void *AddressDeathAction(void *p) { +forceinline void *AddressDeathAction(void *p) { #if MALLOC_TRACE (dprintf)(2, "DEATH %p\n", p); - if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) { + if (weaken(ShowBacktrace)) { + weaken(ShowBacktrace)(2, 0); + } else if (weaken(PrintBacktraceUsingSymbols) && weaken(GetSymbolTable)) { weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0), weaken(GetSymbolTable)()); } @@ -766,8 +762,8 @@ static inline void *AddressDeathAction(void *p) { that may be needed to place segment records and fenceposts when new noncontiguous segments are added. */ -#define TOP_FOOT_SIZE \ - (align_offset(chunk2mem(0)) + pad_request(sizeof(struct malloc_segment)) + \ +#define TOP_FOOT_SIZE \ + (align_offset(chunk2mem(0)) + pad_request(sizeof(struct MallocSegment)) + \ MIN_CHUNK_SIZE) /* ───────────── Global MallocState and MallocParams ─────────────────── */ @@ -1261,26 +1257,26 @@ forceinline msegmentptr segment_holding(mstate m, char *addr) { that may be needed to place segment records and fenceposts when new noncontiguous segments are added. */ -#define TOP_FOOT_SIZE \ - (align_offset(chunk2mem(0)) + pad_request(sizeof(struct malloc_segment)) + \ +#define TOP_FOOT_SIZE \ + (align_offset(chunk2mem(0)) + pad_request(sizeof(struct MallocSegment)) + \ MIN_CHUNK_SIZE) /* ────────────────────────── Debugging setup ──────────────────────────── */ -#if !(DEBUG + MODE_DBG + 0) -#define check_free_chunk(M, P) -#define check_inuse_chunk(M, P) -#define check_malloced_chunk(M, P, N) -#define check_mmapped_chunk(M, P) -#define check_malloc_state(M) -#define check_top_chunk(M, P) -#else /* DEBUG */ +#ifdef DEBUG #define check_free_chunk(M, P) do_check_free_chunk(M, P) #define check_inuse_chunk(M, P) do_check_inuse_chunk(M, P) #define check_top_chunk(M, P) do_check_top_chunk(M, P) #define check_malloced_chunk(M, P, N) do_check_malloced_chunk(M, P, N) #define check_mmapped_chunk(M, P) do_check_mmapped_chunk(M, P) #define check_malloc_state(M) do_check_malloc_state(M) +#else +#define check_free_chunk(M, P) +#define check_inuse_chunk(M, P) +#define check_malloced_chunk(M, P, N) +#define check_mmapped_chunk(M, P) +#define check_malloc_state(M) +#define check_top_chunk(M, P) #endif /* DEBUG */ void do_check_free_chunk(mstate, mchunkptr) hidden; @@ -1292,18 +1288,16 @@ void do_check_malloc_state(mstate) hidden; /* ─────────────────────────── prototypes ──────────────────────────────── */ -void *dlmalloc(size_t) hidden; -void *dlcalloc(size_t, size_t) hidden; +void *dlmalloc(size_t) hidden attributeallocsize((1)) mallocesque; +void *dlcalloc(size_t, size_t) hidden attributeallocsize((1, 2)) mallocesque; void dlfree(void *) nothrow nocallback hidden; -void *dlmemalign$impl(mstate, size_t, size_t) hidden; -void *dlrealloc(void *, size_t) hidden; -void *dlrealloc_in_place(void *, size_t) hidden; -void *dlvalloc(size_t) hidden; -void *dlpvalloc(size_t) hidden; -void *dlmemalign(size_t, size_t) hidden; +void *dlmemalign_impl(mstate, size_t, size_t) hidden; +void *dlrealloc(void *, size_t) hidden reallocesque; +void *dlrealloc_in_place(void *, size_t) hidden reallocesque; +void *dlmemalign(size_t, size_t) hidden attributeallocalign((1)) + attributeallocsize((2)) returnspointerwithnoaliases libcesque nodiscard; int dlmalloc_trim(size_t) hidden; size_t dlmalloc_usable_size(const void *) hidden; -int dlposix_memalign(void **, size_t, size_t) hidden; void **dlindependent_calloc(size_t, size_t, void *[]) hidden; void **dlindependent_comalloc(size_t, size_t[], void *[]) hidden; struct MallocStats dlmalloc_stats(mstate) hidden; diff --git a/third_party/dlmalloc/dlmalloc.mk b/third_party/dlmalloc/dlmalloc.mk index ede7b0bb28c..2e0d6662c5a 100644 --- a/third_party/dlmalloc/dlmalloc.mk +++ b/third_party/dlmalloc/dlmalloc.mk @@ -52,6 +52,12 @@ $(THIRD_PARTY_DLMALLOC_A_OBJS): \ $(NO_MAGIC) \ -fno-sanitize=address +ifneq ($(MODE),dbg) +$(THIRD_PARTY_DLMALLOC_A_OBJS): \ + OVERRIDE_CFLAGS += \ + -DNDEBUG +endif + THIRD_PARTY_DLMALLOC_LIBS = $(foreach x,$(THIRD_PARTY_DLMALLOC_ARTIFACTS),$($(x))) THIRD_PARTY_DLMALLOC_SRCS = $(foreach x,$(THIRD_PARTY_DLMALLOC_ARTIFACTS),$($(x)_SRCS)) THIRD_PARTY_DLMALLOC_HDRS = $(foreach x,$(THIRD_PARTY_DLMALLOC_ARTIFACTS),$($(x)_HDRS)) diff --git a/third_party/dlmalloc/dlmalloc_stats.c b/third_party/dlmalloc/dlmalloc_stats.c index ec342190203..2923dac86dd 100644 --- a/third_party/dlmalloc/dlmalloc_stats.c +++ b/third_party/dlmalloc/dlmalloc_stats.c @@ -21,6 +21,7 @@ * More information can be obtained by calling mallinfo. */ struct MallocStats dlmalloc_stats(mstate m) { + struct MallocChunk *q; struct MallocStats res; bzero(&res, sizeof(res)); ensure_initialization(); @@ -32,7 +33,7 @@ struct MallocStats dlmalloc_stats(mstate m) { res.fp = m->footprint; res.used = res.fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { - mchunkptr q = align_as_chunk(s->base); + q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { if (!is_inuse(q)) res.used -= chunksize(q); diff --git a/third_party/dlmalloc/dlposix_memalign.c b/third_party/dlmalloc/dlposix_memalign.c deleted file mode 100644 index 2f50d2de8e5..00000000000 --- a/third_party/dlmalloc/dlposix_memalign.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "libc/errno.h" -#include "libc/mem/mem.h" -#include "libc/sysv/errfuns.h" -#include "third_party/dlmalloc/dlmalloc.internal.h" - -int dlposix_memalign(void** pp, size_t alignment, size_t bytes) { - void* mem; - size_t d, r; - mem = NULL; - if (alignment == MALLOC_ALIGNMENT) { - mem = dlmalloc(bytes); - } else { - d = alignment / sizeof(void*); - r = alignment % sizeof(void*); - if (r != 0 || d == 0 || (d & (d - SIZE_T_ONE)) != 0) { - return einval(); - } else if (bytes <= MAX_REQUEST - alignment) { - if (alignment < MIN_CHUNK_SIZE) alignment = MIN_CHUNK_SIZE; - mem = dlmemalign$impl(g_dlmalloc, alignment, bytes); - } - } - if (mem == 0) { - return enomem(); - } else { - *pp = mem; - return 0; - } -} diff --git a/third_party/dlmalloc/dlpvalloc.c b/third_party/dlmalloc/dlpvalloc.c deleted file mode 100644 index ac81aa18f1a..00000000000 --- a/third_party/dlmalloc/dlpvalloc.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "libc/mem/mem.h" -#include "third_party/dlmalloc/dlmalloc.internal.h" - -void *dlpvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = g_mparams.page_size; - return dlmemalign(pagesz, - (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); -} diff --git a/third_party/dlmalloc/dlrealloc.c b/third_party/dlmalloc/dlrealloc.c index 32229258330..22f649fcf7a 100644 --- a/third_party/dlmalloc/dlrealloc.c +++ b/third_party/dlmalloc/dlrealloc.c @@ -1,42 +1,47 @@ +#include "libc/bits/likely.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" #include "third_party/dlmalloc/dlmalloc.internal.h" void *dlrealloc(void *oldmem, size_t bytes) { void *mem = 0; - if (oldmem == 0) { - mem = dlmalloc(bytes); - } else if (bytes >= MAX_REQUEST) { - enomem(); - } else if (bytes == 0) { - dlfree(oldmem); - } else { - size_t nb = request2size(bytes); - mchunkptr oldp = mem2chunk(oldmem); + size_t oc, nb; + struct MallocState *m; + struct MallocChunk *oldp, *newp; + if (oldmem) { + if (LIKELY(bytes < MAX_REQUEST)) { + if (bytes) { + nb = request2size(bytes); + oldp = mem2chunk(oldmem); #if !FOOTERS - mstate m = g_dlmalloc; -#else /* FOOTERS */ - mstate m = get_mstate_for(oldp); - if (!ok_magic(m)) { - USAGE_ERROR_ACTION(m, oldmem); - return 0; - } + m = g_dlmalloc; +#else /* FOOTERS */ + m = get_mstate_for(oldp); + if (UNLIKELY(!ok_magic(m))) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } #endif /* FOOTERS */ - if (!PREACTION(m)) { - mchunkptr newp = dlmalloc_try_realloc_chunk(m, oldp, nb, 1); - POSTACTION(m); - if (newp != 0) { - check_inuse_chunk(m, newp); - mem = chunk2mem(newp); - } else { - mem = dlmalloc(bytes); - if (mem != 0) { - size_t oc = chunksize(oldp) - overhead_for(oldp); - memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); - dlfree(oldmem); + if (!PREACTION(m)) { + newp = dlmalloc_try_realloc_chunk(m, oldp, nb, 1); + POSTACTION(m); + if (newp) { + check_inuse_chunk(m, newp); + mem = chunk2mem(newp); + } else if ((mem = dlmalloc(bytes))) { + oc = chunksize(oldp) - overhead_for(oldp); + memcpy(mem, oldmem, (oc < bytes) ? oc : bytes); + dlfree(oldmem); + } } + } else { + dlfree(oldmem); } + } else { + enomem(); } + } else { + mem = dlmalloc(bytes); } return mem; } diff --git a/third_party/dlmalloc/dlvalloc.c b/third_party/dlmalloc/dlvalloc.c deleted file mode 100644 index ee75da4c75a..00000000000 --- a/third_party/dlmalloc/dlvalloc.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "libc/mem/mem.h" -#include "third_party/dlmalloc/dlmalloc.internal.h" - -void *dlvalloc(size_t bytes) { - size_t pagesz; - ensure_initialization(); - pagesz = g_mparams.page_size; - return dlmemalign(pagesz, bytes); -} diff --git a/third_party/dlmalloc/malloc_inspect_all.c b/third_party/dlmalloc/malloc_inspect_all.c index 9fc415ca4fc..f1fa6e938af 100644 --- a/third_party/dlmalloc/malloc_inspect_all.c +++ b/third_party/dlmalloc/malloc_inspect_all.c @@ -2,10 +2,10 @@ #include "third_party/dlmalloc/dlmalloc.internal.h" static void internal_inspect_all(mstate m, - void (*handler)(void* start, void* end, + void (*handler)(void *start, void *end, size_t used_bytes, - void* callback_arg), - void* arg) { + void *callback_arg), + void *arg) { if (is_initialized(m)) { mchunkptr top = m->top; msegmentptr s; @@ -15,20 +15,21 @@ static void internal_inspect_all(mstate m, mchunkptr next = next_chunk(q); size_t sz = chunksize(q); size_t used; - void* start; + void *start; if (is_inuse(q)) { used = sz - CHUNK_OVERHEAD; /* must not be mmapped */ start = chunk2mem(q); } else { used = 0; if (is_small(sz)) { /* offset by possible bookkeeping */ - start = (void*)((char*)q + sizeof(struct malloc_chunk)); + start = (void *)((char *)q + sizeof(struct MallocChunk)); } else { - start = (void*)((char*)q + sizeof(struct malloc_tree_chunk)); + start = (void *)((char *)q + sizeof(struct MallocTreeChunk)); } } - if (start < (void*)next) /* skip if all space is bookkeeping */ + if (start < (void *)next) { /* skip if all space is bookkeeping */ handler(start, next, used, arg); + } if (q == top) break; q = next; } @@ -60,9 +61,9 @@ static void internal_inspect_all(mstate m, * * malloc_inspect_all(count_chunks, NULL); */ -void malloc_inspect_all(void (*handler)(void* start, void* end, - size_t used_bytes, void* callback_arg), - void* arg) { +void malloc_inspect_all(void (*handler)(void *start, void *end, + size_t used_bytes, void *callback_arg), + void *arg) { ensure_initialization(); if (!PREACTION(g_dlmalloc)) { internal_inspect_all(g_dlmalloc, handler, arg); diff --git a/third_party/dlmalloc/malloc_trim.c b/third_party/dlmalloc/malloc_trim.c index caf6fe756c1..e6982a8a85b 100644 --- a/third_party/dlmalloc/malloc_trim.c +++ b/third_party/dlmalloc/malloc_trim.c @@ -21,6 +21,7 @@ * @return 1 if it actually released any memory, else 0 */ int dlmalloc_trim(size_t pad) { + /* asan runtime depends on this function */ int result = 0; ensure_initialization(); if (!PREACTION(g_dlmalloc)) { diff --git a/third_party/gdtoa/dmisc.c b/third_party/gdtoa/dmisc.c index c2dc60febd3..9f7b99a38f8 100644 --- a/third_party/gdtoa/dmisc.c +++ b/third_party/gdtoa/dmisc.c @@ -32,8 +32,16 @@ #include "third_party/gdtoa/gdtoa.internal.h" /* clang-format off */ +void +freedtoa(char *s) +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + __gdtoa_Bfree(b); +} + char * -rv_alloc(int i) +__gdtoa_rv_alloc(int i) { int j, k, *r; j = sizeof(ULong); @@ -41,16 +49,16 @@ rv_alloc(int i) (int)(sizeof(Bigint) - sizeof(ULong) - sizeof(int)) + j <= i; j <<= 1) k++; - r = (int*)Balloc(k); + r = (int*)__gdtoa_Balloc(k); *r = k; return (char *)(r+1); } char * -nrv_alloc(char *s, char **rve, int n) +__gdtoa_nrv_alloc(char *s, char **rve, int n) { char *rv, *t; - t = rv = rv_alloc(n); + t = rv = __gdtoa_rv_alloc(n); while((*t = *s++) !=0) t++; if (rve) @@ -58,16 +66,8 @@ nrv_alloc(char *s, char **rve, int n) return rv; } -void -freedtoa(char *s) -{ - Bigint *b = (Bigint *)((int *)s - 1); - b->maxwds = 1 << (b->k = *(int*)b); - Bfree(b); -} - int -quorem(Bigint *b, Bigint *S) +__gdtoa_quorem(Bigint *b, Bigint *S) { int n; ULong *bx, *bxe, q, *sx, *sxe; @@ -75,7 +75,7 @@ quorem(Bigint *b, Bigint *S) n = S->wds; #ifdef DEBUG if (b->wds > n) - Bug("oversize b in quorem"); + Bug("oversize b in __gdtoa_quorem"); #endif if (b->wds < n) return 0; @@ -86,7 +86,7 @@ quorem(Bigint *b, Bigint *S) q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ #ifdef DEBUG if (q > 9) - Bug("oversized quotient in quorem"); + Bug("oversized quotient in __gdtoa_quorem"); #endif if (q) { borrow = 0; @@ -94,9 +94,9 @@ quorem(Bigint *b, Bigint *S) do { ys = *sx++ * (ULLong)q + carry; carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; + y = *bx - (ys & 0xffffffff) - borrow; borrow = y >> 32 & 1UL; - *bx++ = y & 0xffffffffUL; + *bx++ = y & 0xffffffff; } while(sx <= sxe); if (!*bxe) { @@ -106,7 +106,7 @@ quorem(Bigint *b, Bigint *S) b->wds = n; } } - if (cmp(b, S) >= 0) { + if (__gdtoa_cmp(b, S) >= 0) { q++; borrow = 0; carry = 0; @@ -115,9 +115,9 @@ quorem(Bigint *b, Bigint *S) do { ys = *sx++ + carry; carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; + y = *bx - (ys & 0xffffffff) - borrow; borrow = y >> 32 & 1UL; - *bx++ = y & 0xffffffffUL; + *bx++ = y & 0xffffffff; } while(sx <= sxe); bx = b->x; diff --git a/third_party/gdtoa/dtoa.c b/third_party/gdtoa/dtoa.c index 7d66a0938f5..88eeeb995fe 100644 --- a/third_party/gdtoa/dtoa.c +++ b/third_party/gdtoa/dtoa.c @@ -41,12 +41,12 @@ * Modifications: * 1. Rather than iterating, we use a simple numeric overestimate * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. + * quantities using O(log2(k)) rather than O(k) __gdtoa_multiplications. * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't * try to generate digits strictly left to right. Instead, we * compute with fewer bits and propagate the carry if necessary * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, + * 3. Under the as__gdtoa_sumption that input will be rounded nearest, * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. * That is, we allow equality in stopping tests when the * round-nearest rule will give the same floating-point value @@ -56,10 +56,10 @@ * quantities. * 5. When converting floating-point integers less than 1e16, * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. + * to __gdtoa_multiple-precision integers. * 6. When asked to produce fewer than 15 digits, we first try * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot + * __gdtoa_multiple-precision integer arithmetic only if we cannot * guarantee that the floating-point calculation has given * the correctly rounded result. For k requested digits and * "uniformly" distributed input, the probability is @@ -128,12 +128,12 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) /* Infinity or NaN */ *decpt = 9999; if (!word1(&d) && !(word0(&d) & 0xfffff)) - return nrv_alloc("Infinity", rve, 8); - return nrv_alloc("NaN", rve, 3); + return __gdtoa_nrv_alloc("Infinity", rve, 8); + return __gdtoa_nrv_alloc("NaN", rve, 3); } if (!dval(&d)) { *decpt = 1; - return nrv_alloc("0", rve, 1); + return __gdtoa_nrv_alloc("0", rve, 1); } if (Rounding >= 2) { if (*sign) @@ -142,7 +142,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) if (Rounding != 2) Rounding = 0; } - b = d2b(dval(&d), &be, &bbits); + b = __gdtoa_d2b(dval(&d), &be, &bbits); if (( i = (int)(word0(&d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)) )!=0) { dval(&d2) = dval(&d); word0(&d2) &= Frac_mask1; @@ -160,7 +160,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) * We want k to be too large rather than too small. * The error in the first-order Taylor series approximation * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of + * to compensate for any error in the __gdtoa_multiplication of * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, * adding 1e-13 to the constant term more than suffices. @@ -187,7 +187,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) k--; /* want k = floor(ds) */ k_check = 1; if (k >= 0 && k <= Ten_pmax) { - if (dval(&d) < tens[k]) + if (dval(&d) < __gdtoa_tens[k]) k--; k_check = 0; } @@ -245,7 +245,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) if (i <= 0) i = 1; } - s = s0 = rv_alloc(i); + s = s0 = __gdtoa_rv_alloc(i); if (mode > 1 && Rounding != 1) leftright = 0; if (ilim >= 0 && ilim <= Quick_max && try_quick) { @@ -257,27 +257,27 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) ilim0 = ilim; ieps = 2; /* conservative */ if (k > 0) { - ds = tens[k&0xf]; + ds = __gdtoa_tens[k&0xf]; j = k >> 4; if (j & Bletch) { /* prevent overflows */ j &= Bletch - 1; - dval(&d) /= bigtens[n_bigtens-1]; + dval(&d) /= __gdtoa_bigtens[n___gdtoa_bigtens-1]; ieps++; } for(; j; j >>= 1, i++) if (j & 1) { ieps++; - ds *= bigtens[i]; + ds *= __gdtoa_bigtens[i]; } dval(&d) /= ds; } else if (( j1 = -k )!=0) { - dval(&d) *= tens[j1 & 0xf]; + dval(&d) *= __gdtoa_tens[j1 & 0xf]; for(j = j1 >> 4; j; j >>= 1, i++) if (j & 1) { ieps++; - dval(&d) *= bigtens[i]; + dval(&d) *= __gdtoa_bigtens[i]; } } if (k_check && dval(&d) < 1. && ilim > 0) { @@ -303,14 +303,14 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) /* Use Steele & White method of only * generating digits needed. */ - dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); + dval(&eps) = 0.5/__gdtoa_tens[ilim-1] - dval(&eps); if (k0 < 0 && j1 >= 307) { eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */ word0(&eps1) -= Exp_msk1 * (Bias+P-1); - dval(&eps1) *= tens[j1 & 0xf]; + dval(&eps1) *= __gdtoa_tens[j1 & 0xf]; for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++) if (j & 1) - dval(&eps1) *= bigtens[i]; + dval(&eps1) *= __gdtoa_bigtens[i]; if (eps.d < eps1.d) eps.d = eps1.d; if (10. - d.d < 10.*eps.d && eps.d < 1.) { @@ -336,7 +336,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) } else { /* Generate ilim digits, then fix them up. */ - dval(&eps) *= tens[ilim-1]; + dval(&eps) *= __gdtoa_tens[ilim-1]; for(i = 1;; i++, dval(&d) *= 10.) { L = (Long)(dval(&d)); if (!(dval(&d) -= L)) @@ -361,7 +361,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) /* Do we have a "small" integer? */ if (be >= 0 && k <= Int_max) { /* Yes. */ - ds = tens[k]; + ds = __gdtoa_tens[k]; if (ndigits < 0 && ilim <= 0) { S = mhi = 0; if (ilim < 0 || dval(&d) <= 5*ds) @@ -409,7 +409,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) i = denorm ? be + (Bias + (P-1) - 1 + 1) : 1 + P - bbits; b2 += i; s2 += i; - mhi = i2b(1); + mhi = __gdtoa_i2b(1); } if (m2 > 0 && s2 > 0) { i = m2 < s2 ? m2 : s2; @@ -420,20 +420,20 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) if (b5 > 0) { if (leftright) { if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mult(mhi, b); - Bfree(b); + mhi = __gdtoa_pow5mult(mhi, m5); + b1 = __gdtoa_mult(mhi, b); + __gdtoa_Bfree(b); b = b1; } if (( j = b5 - m5 )!=0) - b = pow5mult(b, j); + b = __gdtoa_pow5mult(b, j); } else - b = pow5mult(b, b5); + b = __gdtoa_pow5mult(b, b5); } - S = i2b(1); + S = __gdtoa_i2b(1); if (s5 > 0) - S = pow5mult(S, s5); + S = __gdtoa_pow5mult(S, s5); /* Check for special case that d is a normalized power of 2. */ spec_case = 0; @@ -451,7 +451,7 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) * shift left if necessary so divisor has 4 leading 0 bits. * * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it + * and for all and pass them and a shift to __gdtoa_quorem, so it * can do shifts and ors to compute the numerator for q. */ if (( i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f )!=0) @@ -469,20 +469,20 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) s2 += i; } if (b2 > 0) - b = lshift(b, b2); + b = __gdtoa_lshift(b, b2); if (s2 > 0) - S = lshift(S, s2); + S = __gdtoa_lshift(S, s2); if (k_check) { - if (cmp(b,S) < 0) { + if (__gdtoa_cmp(b,S) < 0) { k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ + b = __gdtoa_multadd(b, 10, 0); /* we botched the k estimate */ if (leftright) - mhi = multadd(mhi, 10, 0); + mhi = __gdtoa_multadd(mhi, 10, 0); ilim = ilim1; } } if (ilim <= 0 && (mode == 3 || mode == 5)) { - if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + if (ilim < 0 || __gdtoa_cmp(b,S = __gdtoa_multadd(S,5,0)) <= 0) { /* no digits, fcvt style */ no_digits: k = -1 - ndigits; @@ -495,25 +495,25 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) } if (leftright) { if (m2 > 0) - mhi = lshift(mhi, m2); + mhi = __gdtoa_lshift(mhi, m2); /* Compute mlo -- check for special case * that d is a normalized power of 2. */ mlo = mhi; if (spec_case) { - mhi = Balloc(mhi->k); + mhi = __gdtoa_Balloc(mhi->k); Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); + mhi = __gdtoa_lshift(mhi, Log2P); } for(i = 1;;i++) { - dig = quorem(b,S) + '0'; + dig = __gdtoa_quorem(b,S) + '0'; /* Do we yet have the shortest decimal string * that will round to d? */ - j = cmp(b, mlo); - delta = diff(S, mhi); - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); + j = __gdtoa_cmp(b, mlo); + delta = __gdtoa_diff(S, mhi); + j1 = delta->sign ? 1 : __gdtoa_cmp(b, delta); + __gdtoa_Bfree(delta); if (j1 == 0 && mode != 1 && !(word1(&d) & 1) && Rounding >= 1) { if (dig == '9') goto round_9_up; @@ -533,8 +533,8 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) case 2: goto keep_dig; } if (j1 > 0) { - b = lshift(b, 1); - j1 = cmp(b, S); + b = __gdtoa_lshift(b, 1); + j1 = __gdtoa_cmp(b, S); if ((j1 > 0 || (j1 == 0 && dig & 1)) && dig++ == '9') goto round_9_up; @@ -558,24 +558,24 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) *s++ = dig; if (i == ilim) break; - b = multadd(b, 10, 0); + b = __gdtoa_multadd(b, 10, 0); if (mlo == mhi) - mlo = mhi = multadd(mhi, 10, 0); + mlo = mhi = __gdtoa_multadd(mhi, 10, 0); else { - mlo = multadd(mlo, 10, 0); - mhi = multadd(mhi, 10, 0); + mlo = __gdtoa_multadd(mlo, 10, 0); + mhi = __gdtoa_multadd(mhi, 10, 0); } } } else { for(i = 1;; i++) { - *s++ = dig = quorem(b,S) + '0'; + *s++ = dig = __gdtoa_quorem(b,S) + '0'; if (!b->x[0] && b->wds <= 1) { goto ret; } if (i >= ilim) break; - b = multadd(b, 10, 0); + b = __gdtoa_multadd(b, 10, 0); } } @@ -584,8 +584,8 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) case 0: goto trimzeros; case 2: goto roundoff; } - b = lshift(b, 1); - j = cmp(b, S); + b = __gdtoa_lshift(b, 1); + j = __gdtoa_cmp(b, S); if (j > 0 || (j == 0 && dig & 1)) { roundoff: @@ -603,17 +603,17 @@ dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) s++; } ret: - Bfree(S); + __gdtoa_Bfree(S); if (mhi) { if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); + __gdtoa_Bfree(mlo); + __gdtoa_Bfree(mhi); } retc: while(s > s0 && s[-1] == '0') --s; ret1: - Bfree(b); + __gdtoa_Bfree(b); *s = 0; *decpt = k + 1; if (rve) diff --git a/third_party/gdtoa/g__fmt.c b/third_party/gdtoa/g__fmt.c index c6fa7419ae9..20f4b5c2aff 100644 --- a/third_party/gdtoa/g__fmt.c +++ b/third_party/gdtoa/g__fmt.c @@ -40,8 +40,8 @@ #define ldus_QNAN3 0 #define ldus_QNAN4 0 -const char *const InfName[6] = { "Infinity", "infinity", "INFINITY", "Inf", "inf", "INF" }; -const char *const NanName[3] = { "NaN", "nan", "NAN" }; +const char *const __gdtoa_InfName[6] = { "Infinity", "infinity", "INFINITY", "Inf", "inf", "INF" }; +const char *const __gdtoa_NanName[3] = { "NaN", "nan", "NAN" }; const ULong __gdtoa_NanDflt_Q[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff }; const ULong __gdtoa_NanDflt_d[2] = { d_QNAN1, d_QNAN0 }; const ULong __gdtoa_NanDflt_f[1] = { f_QNAN }; @@ -49,7 +49,7 @@ const ULong __gdtoa_NanDflt_xL[3] = { 1, 0x80000000, 0x7fff0000 }; const UShort __gdtoa_NanDflt_ldus[5] = { ldus_QNAN4, ldus_QNAN3, ldus_QNAN2, ldus_QNAN1, ldus_QNAN0 }; char * -g__fmt(char *b, char *s, char *se, int decpt, ULong sign, size_t blen) +__gdtoa_g__fmt(char *b, char *s, char *se, int decpt, ULong sign, size_t blen) { int i, j, k; char *be, *s0; diff --git a/third_party/gdtoa/g_ddfmt.c b/third_party/gdtoa/g_ddfmt.c index 3e6b0e9deea..0085c636a8f 100644 --- a/third_party/gdtoa/g_ddfmt.c +++ b/third_party/gdtoa/g_ddfmt.c @@ -84,38 +84,38 @@ g_ddfmt(char *buf, double *dd0, int ndig, size_t bufsize) dd = ddx; L = dd->L; } - z = d2b(dval(&dd[0]), &ex, &bx); + z = __gdtoa_d2b(dval(&dd[0]), &ex, &bx); if (dval(&dd[1]) == 0.) goto no_y; x = z; - y = d2b(dval(&dd[1]), &ey, &by); + y = __gdtoa_d2b(dval(&dd[1]), &ey, &by); if ( (i = ex - ey) !=0) { if (i > 0) { - x = lshift(x, i); + x = __gdtoa_lshift(x, i); ex = ey; } else - y = lshift(y, -i); + y = __gdtoa_lshift(y, -i); } if ((L[1] ^ L[2+1]) & 0x80000000L) { - z = diff(x, y); + z = __gdtoa_diff(x, y); if (L[1] & 0x80000000L) z->sign = 1 - z->sign; } else { - z = sum(x, y); + z = __gdtoa_sum(x, y); if (L[1] & 0x80000000L) z->sign = 1; } - Bfree(x); - Bfree(y); + __gdtoa_Bfree(x); + __gdtoa_Bfree(y); no_y: bits = zx = z->x; for(i = 0; !*zx; zx++) i += 32; i += lo0bits(zx); if (i) { - rshift(z, i); + __gdtoa_rshift(z, i); ex += i; } fpi.nbits = z->wds * 32 - hi0bits(z->x[j = z->wds-1]); @@ -132,7 +132,7 @@ g_ddfmt(char *buf, double *dd0, int ndig, size_t bufsize) mode = 2; if (ndig <= 0) { if (bufsize < (size_t)(fpi.nbits * .301029995664) + 10) { - Bfree(z); + __gdtoa_Bfree(z); return 0; } mode = 0; @@ -144,7 +144,7 @@ g_ddfmt(char *buf, double *dd0, int ndig, size_t bufsize) fpi.int_max = Int_max; i = STRTOG_Normal; s = gdtoa(&fpi, ex, bits, &i, mode, ndig, &decpt, &se); - b = g__fmt(buf, s, se, decpt, z->sign, bufsize); - Bfree(z); + b = __gdtoa_g__fmt(buf, s, se, decpt, z->sign, bufsize); + __gdtoa_Bfree(z); return b; } diff --git a/third_party/gdtoa/g_ddfmt_p.c b/third_party/gdtoa/g_ddfmt_p.c index 9d03084ae72..485fc800b9a 100644 --- a/third_party/gdtoa/g_ddfmt_p.c +++ b/third_party/gdtoa/g_ddfmt_p.c @@ -59,7 +59,7 @@ g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) b = buf; if (sign && nik < 18) *b++ = '-'; - b = stpcpy(b, NanName[nik%3]); + b = stpcpy(b, __gdtoa_NanName[nik%3]); if (nik > 5 && (nik < 12 || L[0] != __gdtoa_NanDflt_d[0] || (L[1] ^ __gdtoa_NanDflt_d[1]) & 0xfffff @@ -69,7 +69,7 @@ g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) bits0[1] = (L[2+1] & 0xfffff) | (L[0] << 20); bits0[2] = (L[0] >> 12) | (L[1] << 20); bits0[3] = (L[1] >> 12) & 0xff; - b = add_nanbits(b, bufsize - (b-buf), bits0, 4); + b = __gdtoa_add_nanbits(b, bufsize - (b-buf), bits0, 4); } return b; } @@ -83,7 +83,7 @@ g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) b = buf; if (L[1] & 0x80000000L) *b++ = '-'; - return stpcpy(b, InfName[nik%6]); + return stpcpy(b, __gdtoa_InfName[nik%6]); } if ((L[2+1] & 0x7ff00000) == 0x7ff00000) { L += 2; @@ -105,38 +105,38 @@ g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) dd = ddx; L = dd->L; } - z = d2b(dval(&dd[0]), &ex, &bx); + z = __gdtoa_d2b(dval(&dd[0]), &ex, &bx); if (dval(&dd[1]) == 0.) goto no_y; x = z; - y = d2b(dval(&dd[1]), &ey, &by); + y = __gdtoa_d2b(dval(&dd[1]), &ey, &by); if ( (i = ex - ey) !=0) { if (i > 0) { - x = lshift(x, i); + x = __gdtoa_lshift(x, i); ex = ey; } else - y = lshift(y, -i); + y = __gdtoa_lshift(y, -i); } if ((L[1] ^ L[2+1]) & 0x80000000L) { - z = diff(x, y); + z = __gdtoa_diff(x, y); if (L[1] & 0x80000000L) z->sign = 1 - z->sign; } else { - z = sum(x, y); + z = __gdtoa_sum(x, y); if (L[1] & 0x80000000L) z->sign = 1; } - Bfree(x); - Bfree(y); + __gdtoa_Bfree(x); + __gdtoa_Bfree(y); no_y: bits = zx = z->x; for(i = 0; !*zx; zx++) i += 32; i += lo0bits(zx); if (i) { - rshift(z, i); + __gdtoa_rshift(z, i); ex += i; } fpi.nbits = z->wds * 32 - hi0bits(z->x[j = z->wds-1]); @@ -153,7 +153,7 @@ g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) mode = 2; if (ndig <= 0) { if (bufsize < (size_t)(fpi.nbits * .301029995664) + 10) { - Bfree(z); + __gdtoa_Bfree(z); return 0; } mode = 0; @@ -165,7 +165,7 @@ g_ddfmt_p(char *buf, double *dd0, int ndig, size_t bufsize, int nik) fpi.int_max = Int_max; i = STRTOG_Normal; s = gdtoa(&fpi, ex, bits, &i, mode, ndig, &decpt, &se); - b = g__fmt(buf, s, se, decpt, z->sign, bufsize); - Bfree(z); + b = __gdtoa_g__fmt(buf, s, se, decpt, z->sign, bufsize); + __gdtoa_Bfree(z); return b; } diff --git a/third_party/gdtoa/g_dfmt.c b/third_party/gdtoa/g_dfmt.c index a6e8b0012a5..a78b4cfc0da 100644 --- a/third_party/gdtoa/g_dfmt.c +++ b/third_party/gdtoa/g_dfmt.c @@ -80,5 +80,5 @@ g_dfmt(char *buf, double *d, int ndig, size_t bufsize) if (sign) i = STRTOG_Normal | STRTOG_Neg; s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); - return g__fmt(buf, s, se, decpt, sign, bufsize); + return __gdtoa_g__fmt(buf, s, se, decpt, sign, bufsize); } diff --git a/third_party/gdtoa/g_dfmt_p.c b/third_party/gdtoa/g_dfmt_p.c index e998ad42c98..36e9f67f3f0 100644 --- a/third_party/gdtoa/g_dfmt_p.c +++ b/third_party/gdtoa/g_dfmt_p.c @@ -58,20 +58,20 @@ g_dfmt_p(char *buf, double *d, int ndig, size_t bufsize, int nik) b = buf; if (L[1] & 0x80000000L && nik < 18) *b++ = '-'; - b = stpcpy(b, NanName[nik%3]); + b = stpcpy(b, __gdtoa_NanName[nik%3]); if (nik > 5 && (nik < 12 || bits[0] != __gdtoa_NanDflt_d[0] || (bits[1] ^ __gdtoa_NanDflt_d[1]) & 0xfffff)) { bits[0] = L[0]; bits[1] = L[1] & 0xfffff; - b = add_nanbits(b, bufsize - (b-buf), bits, 2); + b = __gdtoa_add_nanbits(b, bufsize - (b-buf), bits, 2); } return b; } b = buf; if (sign) *b++ = '-'; - return stpcpy(b, InfName[nik%6]); + return stpcpy(b, __gdtoa_InfName[nik%6]); } if (L[0] == 0 && (L[1] ^ sign) == 0 /*d == 0.*/) { b = buf; @@ -95,5 +95,5 @@ g_dfmt_p(char *buf, double *d, int ndig, size_t bufsize, int nik) if (sign) i = STRTOG_Normal | STRTOG_Neg; s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); - return g__fmt(buf, s, se, decpt, sign, bufsize); + return __gdtoa_g__fmt(buf, s, se, decpt, sign, bufsize); } diff --git a/third_party/gdtoa/g_ffmt.c b/third_party/gdtoa/g_ffmt.c index df17c9600f5..7c76b9bd541 100644 --- a/third_party/gdtoa/g_ffmt.c +++ b/third_party/gdtoa/g_ffmt.c @@ -78,5 +78,5 @@ g_ffmt(char *buf, float *f, int ndig, size_t bufsize) } i = STRTOG_Normal; s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); - return g__fmt(buf, s, se, decpt, sign, bufsize); + return __gdtoa_g__fmt(buf, s, se, decpt, sign, bufsize); } diff --git a/third_party/gdtoa/g_ffmt_p.c b/third_party/gdtoa/g_ffmt_p.c index 48e22858f47..68e0243b93d 100644 --- a/third_party/gdtoa/g_ffmt_p.c +++ b/third_party/gdtoa/g_ffmt_p.c @@ -56,16 +56,16 @@ g_ffmt_p(char *buf, float *f, int ndig, size_t bufsize, int nik) b = buf; if (sign && nik < 18) *b++ = '-'; - b = stpcpy(b, NanName[nik%3]); + b = stpcpy(b, __gdtoa_NanName[nik%3]); if (nik > 5 && (nik < 12 || (bits[0] ^ __gdtoa_NanDflt_f[0]) & 0x7fffff)) - b = add_nanbits(b, bufsize - (b-buf), bits, 1); + b = __gdtoa_add_nanbits(b, bufsize - (b-buf), bits, 1); return b; } b = buf; if (sign) *b++ = '-'; - return stpcpy(b, InfName[nik%6]); + return stpcpy(b, __gdtoa_InfName[nik%6]); } if (*f == 0.) { b = buf; @@ -89,5 +89,5 @@ g_ffmt_p(char *buf, float *f, int ndig, size_t bufsize, int nik) } i = STRTOG_Normal; s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); - return g__fmt(buf, s, se, decpt, sign, bufsize); + return __gdtoa_g__fmt(buf, s, se, decpt, sign, bufsize); } diff --git a/third_party/gdtoa/g_xfmt.c b/third_party/gdtoa/g_xfmt.c index 117ed9c1dce..14f01e0f0cd 100644 --- a/third_party/gdtoa/g_xfmt.c +++ b/third_party/gdtoa/g_xfmt.c @@ -84,5 +84,5 @@ g_xfmt(char *buf, void *V, int ndig, size_t bufsize) mode = 0; } s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); - return g__fmt(buf, s, se, decpt, sign, bufsize); + return __gdtoa_g__fmt(buf, s, se, decpt, sign, bufsize); } diff --git a/third_party/gdtoa/g_xfmt_p.c b/third_party/gdtoa/g_xfmt_p.c index 35906e34e76..c2270424869 100644 --- a/third_party/gdtoa/g_xfmt_p.c +++ b/third_party/gdtoa/g_xfmt_p.c @@ -60,20 +60,20 @@ g_xfmt_p(char *buf, void *V, int ndig, size_t bufsize, int nik) b = buf; if (sign) *b++ = '-'; - b = stpcpy(b, InfName[nik%6]); + b = stpcpy(b, __gdtoa_InfName[nik%6]); } else { b = buf; if (sign && nik < 18) *b++ = '-'; - b = stpcpy(b, NanName[nik%3]); + b = stpcpy(b, __gdtoa_NanName[nik%3]); if (nik > 5 && (nik < 12 || L[3] != __gdtoa_NanDflt_ldus[3] || L[2] != __gdtoa_NanDflt_ldus[2] || L[1] != __gdtoa_NanDflt_ldus[1] || L[0] != __gdtoa_NanDflt_ldus[0])) { bits[1] &= 0x7fffffff; - b = add_nanbits(b, bufsize - (b-buf), bits, 2); + b = __gdtoa_add_nanbits(b, bufsize - (b-buf), bits, 2); } } return b; @@ -100,5 +100,5 @@ g_xfmt_p(char *buf, void *V, int ndig, size_t bufsize, int nik) mode = 0; } s = gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); - return g__fmt(buf, s, se, decpt, sign, bufsize); + return __gdtoa_g__fmt(buf, s, se, decpt, sign, bufsize); } diff --git a/third_party/gdtoa/gdtoa.c b/third_party/gdtoa/gdtoa.c index 2d0dc45dd25..1f5d994d0a1 100644 --- a/third_party/gdtoa/gdtoa.c +++ b/third_party/gdtoa/gdtoa.c @@ -44,7 +44,7 @@ bitstob(ULong *bits, int nbits, int *bbits) i <<= 1; k++; } - b = Balloc(k); + b = __gdtoa_Balloc(k); be = bits + ((nbits - 1) >> kshift); x = x0 = b->x; do { @@ -71,12 +71,12 @@ bitstob(ULong *bits, int nbits, int *bbits) * Modifications: * 1. Rather than iterating, we use a simple numeric overestimate * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. + * quantities using O(log2(k)) rather than O(k) __gdtoa_multiplications. * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't * try to generate digits strictly left to right. Instead, we * compute with fewer bits and propagate the carry if necessary * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, + * 3. Under the as__gdtoa_sumption that input will be rounded nearest, * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. * That is, we allow equality in stopping tests when the * round-nearest rule will give the same floating-point value @@ -86,10 +86,10 @@ bitstob(ULong *bits, int nbits, int *bbits) * quantities. * 5. When converting floating-point integers less than 1e16, * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. + * to __gdtoa_multiple-precision integers. * 6. When asked to produce fewer than 15 digits, we first try * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot + * __gdtoa_multiple-precision integer arithmetic only if we cannot * guarantee that the floating-point calculation has given * the correctly rounded result. For k requested digits and * "uniformly" distributed input, the probability is @@ -125,7 +125,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in 2 + (mode & 1). These modes are mainly for debugging; often they run slower but sometimes faster than modes 2-3. - 4,5,8,9 ==> left-to-right digit generation. + 4,5,8,9 ==> left-to-right digit gene__gdtoa_ration. 6-9 ==> don't try fast floating-point estimate (if applicable). @@ -153,27 +153,27 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in break; case STRTOG_Infinite: *decpt = -32768; - return nrv_alloc("Infinity", rve, 8); + return __gdtoa_nrv_alloc("Infinity", rve, 8); case STRTOG_NaN: *decpt = -32768; - return nrv_alloc("NaN", rve, 3); + return __gdtoa_nrv_alloc("NaN", rve, 3); default: return 0; } b = bitstob(bits, nbits = fpi->nbits, &bbits); be0 = be; - if ( (i = trailz(b)) !=0) { - rshift(b, i); + if ( (i = __gdtoa_trailz(b)) !=0) { + __gdtoa_rshift(b, i); be += i; bbits -= i; } if (!b->wds) { - Bfree(b); + __gdtoa_Bfree(b); ret_zero: *decpt = 1; - return nrv_alloc("0", rve, 1); + return __gdtoa_nrv_alloc("0", rve, 1); } - dval(&d) = b2d(b, &i); + dval(&d) = __gdtoa_b2d(b, &i); i = be + bbits - 1; word0(&d) &= Frac_mask1; word0(&d) |= Exp_11; @@ -190,7 +190,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in * We want k to be too large rather than too small. * The error in the first-order Taylor series approximation * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of + * to compensate for any error in the __gdtoa_multiplication of * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, * adding 1e-13 to the constant term more than suffices. @@ -199,7 +199,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in * but this is probably not worthwhile.) */ ds = (dval(&d)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - /* correct assumption about exponent range */ + /* correct as__gdtoa_sumption about exponent range */ if ((j = i) < 0) j = -j; if ((j -= 1077) > 0) @@ -226,7 +226,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in // 401173: _start at libc/crt/crt.S:67 word0(&d) += (unsigned)(be + bbits - 1) << Exp_shift; if (k >= 0 && k <= Ten_pmax) { - if (dval(&d) < tens[k]) + if (dval(&d) < __gdtoa_tens[k]) k--; k_check = 0; } @@ -285,7 +285,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in if (i <= 0) i = 1; } - s = s0 = rv_alloc(i); + s = s0 = __gdtoa_rv_alloc(i); if (mode <= 1) rdir = 0; else if ( (rdir = fpi->rounding - 1) !=0) { @@ -303,28 +303,28 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in ilim0 = ilim; ieps = 2; /* conservative */ if (k > 0) { - ds = tens[k&0xf]; + ds = __gdtoa_tens[k&0xf]; j = k >> 4; if (j & Bletch) { /* prevent overflows */ j &= Bletch - 1; - dval(&d) /= bigtens[n_bigtens-1]; + dval(&d) /= __gdtoa_bigtens[n___gdtoa_bigtens-1]; ieps++; } for(; j; j >>= 1, i++) if (j & 1) { ieps++; - ds *= bigtens[i]; + ds *= __gdtoa_bigtens[i]; } } else { ds = 1.; if ( (j1 = -k) !=0) { - dval(&d) *= tens[j1 & 0xf]; + dval(&d) *= __gdtoa_tens[j1 & 0xf]; for(j = j1 >> 4; j; j >>= 1, i++) if (j & 1) { ieps++; - dval(&d) *= bigtens[i]; + dval(&d) *= __gdtoa_bigtens[i]; } } } @@ -351,7 +351,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in /* Use Steele & White method of only * generating digits needed. */ - dval(&eps) = ds*0.5/tens[ilim-1] - dval(&eps); + dval(&eps) = ds*0.5/__gdtoa_tens[ilim-1] - dval(&eps); for(i = 0;;) { L = (Long)(dval(&d)/ds); dval(&d) -= L*ds; @@ -371,7 +371,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in } else { /* Generate ilim digits, then fix them up. */ - dval(&eps) *= tens[ilim-1]; + dval(&eps) *= __gdtoa_tens[ilim-1]; for(i = 1;; i++, dval(&d) *= 10.) { if ( (L = (Long)(dval(&d)/ds)) !=0) dval(&d) -= L*ds; @@ -398,7 +398,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in /* Do we have a "small" integer? */ if (be >= 0 && k <= fpi->int_max) { /* Yes. */ - ds = tens[k]; + ds = __gdtoa_tens[k]; if (ndigits < 0 && ilim <= 0) { S = mhi = 0; if (ilim < 0 || dval(&d) <= 5*ds) @@ -471,7 +471,7 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in } b2 += i; s2 += i; - mhi = i2b(1); + mhi = __gdtoa_i2b(1); } if (m2 > 0 && s2 > 0) { i = m2 < s2 ? m2 : s2; @@ -482,20 +482,20 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in if (b5 > 0) { if (leftright) { if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mult(mhi, b); - Bfree(b); + mhi = __gdtoa_pow5mult(mhi, m5); + b1 = __gdtoa_mult(mhi, b); + __gdtoa_Bfree(b); b = b1; } if ( (j = b5 - m5) !=0) - b = pow5mult(b, j); + b = __gdtoa_pow5mult(b, j); } else - b = pow5mult(b, b5); + b = __gdtoa_pow5mult(b, b5); } - S = i2b(1); + S = __gdtoa_i2b(1); if (s5 > 0) - S = pow5mult(S, s5); + S = __gdtoa_pow5mult(S, s5); /* Check for special case that d is a normalized power of 2. */ spec_case = 0; if (mode < 2) { @@ -510,26 +510,26 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in * shift left if necessary so divisor has 4 leading 0 bits. * * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it + * and for all and pass them and a shift to __gdtoa_quorem, so it * can do shifts and ors to compute the numerator for q. */ i = ((s5 ? hi0bits(S->x[S->wds-1]) : ULbits - 1) - s2 - 4) & kmask; m2 += i; if ((b2 += i) > 0) - b = lshift(b, b2); + b = __gdtoa_lshift(b, b2); if ((s2 += i) > 0) - S = lshift(S, s2); + S = __gdtoa_lshift(S, s2); if (k_check) { - if (cmp(b,S) < 0) { + if (__gdtoa_cmp(b,S) < 0) { k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ + b = __gdtoa_multadd(b, 10, 0); /* we botched the k estimate */ if (leftright) - mhi = multadd(mhi, 10, 0); + mhi = __gdtoa_multadd(mhi, 10, 0); ilim = ilim1; } } if (ilim <= 0 && mode > 2) { - if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + if (ilim < 0 || __gdtoa_cmp(b,S = __gdtoa_multadd(S,5,0)) <= 0) { /* no digits, fcvt style */ no_digits: k = -1 - ndigits; @@ -544,25 +544,25 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in } if (leftright) { if (m2 > 0) - mhi = lshift(mhi, m2); + mhi = __gdtoa_lshift(mhi, m2); /* Compute mlo -- check for special case * that d is a normalized power of 2. */ mlo = mhi; if (spec_case) { - mhi = Balloc(mhi->k); + mhi = __gdtoa_Balloc(mhi->k); Bcopy(mhi, mlo); - mhi = lshift(mhi, 1); + mhi = __gdtoa_lshift(mhi, 1); } for(i = 1;;i++) { - dig = quorem(b,S) + '0'; + dig = __gdtoa_quorem(b,S) + '0'; /* Do we yet have the shortest decimal string * that will round to d? */ - j = cmp(b, mlo); - delta = diff(S, mhi); - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); + j = __gdtoa_cmp(b, mlo); + delta = __gdtoa_diff(S, mhi); + j1 = delta->sign ? 1 : __gdtoa_cmp(b, delta); + __gdtoa_Bfree(delta); if (j1 == 0 && !mode && !(bits[0] & 1) && !rdir) { if (dig == '9') goto round_9_up; @@ -583,14 +583,14 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in inex = STRTOG_Inexlo; goto accept; } - while (cmp(S,mhi) > 0) { + while (__gdtoa_cmp(S,mhi) > 0) { *s++ = dig; - mhi1 = multadd(mhi, 10, 0); + mhi1 = __gdtoa_multadd(mhi, 10, 0); if (mlo == mhi) mlo = mhi1; mhi = mhi1; - b = multadd(b, 10, 0); - dig = quorem(b,S) + '0'; + b = __gdtoa_multadd(b, 10, 0); + dig = __gdtoa_quorem(b,S) + '0'; } if (dig++ == '9') goto round_9_up; @@ -598,8 +598,8 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in goto accept; } if (j1 > 0) { - b = lshift(b, 1); - j1 = cmp(b, S); + b = __gdtoa_lshift(b, 1); + j1 = __gdtoa_cmp(b, S); if ((j1 > 0 || (j1 == 0 && dig & 1)) && dig++ == '9') goto round_9_up; inex = STRTOG_Inexhi; @@ -624,21 +624,21 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in *s++ = dig; if (i == ilim) break; - b = multadd(b, 10, 0); + b = __gdtoa_multadd(b, 10, 0); if (mlo == mhi) - mlo = mhi = multadd(mhi, 10, 0); + mlo = mhi = __gdtoa_multadd(mhi, 10, 0); else { - mlo = multadd(mlo, 10, 0); - mhi = multadd(mhi, 10, 0); + mlo = __gdtoa_multadd(mlo, 10, 0); + mhi = __gdtoa_multadd(mhi, 10, 0); } } } else for(i = 1;; i++) { - *s++ = dig = quorem(b,S) + '0'; + *s++ = dig = __gdtoa_quorem(b,S) + '0'; if (i >= ilim) break; - b = multadd(b, 10, 0); + b = __gdtoa_multadd(b, 10, 0); } /* Round off last digit */ if (rdir) { @@ -646,8 +646,8 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in goto chopzeros; goto roundoff; } - b = lshift(b, 1); - j = cmp(b, S); + b = __gdtoa_lshift(b, 1); + j = __gdtoa_cmp(b, S); if (j > 0 || (j == 0 && dig & 1)) { roundoff: @@ -666,16 +666,16 @@ gdtoa(const FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, in inex = STRTOG_Inexlo; } ret: - Bfree(S); + __gdtoa_Bfree(S); if (mhi) { if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); + __gdtoa_Bfree(mlo); + __gdtoa_Bfree(mhi); } ret1: while(s > s0 && s[-1] == '0') --s; - Bfree(b); + __gdtoa_Bfree(b); *s = 0; *decpt = k + 1; if (rve) diff --git a/third_party/gdtoa/gdtoa.internal.h b/third_party/gdtoa/gdtoa.internal.h index 433bbc1415e..146120c11e1 100644 --- a/third_party/gdtoa/gdtoa.internal.h +++ b/third_party/gdtoa/gdtoa.internal.h @@ -169,9 +169,6 @@ asm(".include \"libc/disclaimer.inc\""); * floating-point constants. * #define -DNO_ERRNO to suppress setting errno (in strtod.c and * strtodg.c). - * #define NO_STRING_H to use private versions of memcpy. - * On some K&R systems, it may also be necessary to - * #define DECLARE_SIZE_T in this case. * #define USE_LOCALE to use the current locale's decimal_point value. */ @@ -197,24 +194,6 @@ typedef unsigned short UShort; } #endif -/* #ifdef KR_headers */ -/* #define Char char */ -/* #else */ -#define Char void -/* #endif */ - -#ifdef MALLOC -extern Char *MALLOC(size_t); -#else -#define MALLOC malloc -#endif - -#ifdef REALLOC -extern Char *REALLOC(Char *, size_t); -#else -#define REALLOC realloc -#endif - #undef IEEE_Arith #undef Avoid_Underflow #ifdef IEEE_MC68k @@ -230,16 +209,8 @@ extern Char *REALLOC(Char *, size_t); #endif /* Bad_float_h */ #ifdef IEEE_Arith -#define Scale_Bit 0x10 -#define n_bigtens 5 -#endif - -#ifdef IBM -#define n_bigtens 3 -#endif - -#ifdef VAX -#define n_bigtens 2 +#define Scale_Bit 0x10 +#define n___gdtoa_bigtens 5 #endif typedef union { @@ -334,7 +305,7 @@ extern double rnd_prod(double, double), rnd_quot(double, double); #define Pack_16 /* When Pack_32 is not defined, we store 16 bits per 32-bit Long. * This makes some inner loops simpler and sometimes saves work - * during multiplications, but it often seems to make things slightly + * during __gdtoa_multiplications, but it often seems to make things slightly * slower. Hence the default is now to store 32 bits per Long. */ #endif @@ -374,111 +345,56 @@ typedef struct ThInfo { Bigint *P5s; } ThInfo; -#ifdef NO_STRING_H -#ifdef DECLARE_SIZE_T -typedef unsigned int size_t; -#endif -extern void __gdtoa_memcpy(void *, const void *, size_t); -#define Bcopy(x, y) \ - __gdtoa_memcpy(&x->sign, &y->sign, y->wds * sizeof(ULong) + 2 * sizeof(int)) -#else /* !NO_STRING_H */ #define Bcopy(x, y) \ memcpy(&x->sign, &y->sign, y->wds * sizeof(ULong) + 2 * sizeof(int)) -#endif /* NO_STRING_H */ - -#define Balloc __gdtoa_Balloc -#define Bfree __gdtoa_Bfree -#define InfName __gdtoa_InfName -#define NanName __gdtoa_NanName -#define ULtoQ __gdtoa_ULtoQ -#define ULtof __gdtoa_ULtof -#define ULtod __gdtoa_ULtod -#define ULtodd __gdtoa_ULtodd -#define ULtox __gdtoa_ULtox -#define ULtoxL __gdtoa_ULtoxL -#define add_nanbits __gdtoa_add_nanbits -#define any_on __gdtoa_any_on -#define b2d __gdtoa_b2d -#define bigtens __gdtoa_bigtens -#define cmp __gdtoa_cmp -#define copybits __gdtoa_copybits -#define d2b __gdtoa_d2b -#define decrement __gdtoa_decrement -#define diff __gdtoa_diff -#define dtoa_result __gdtoa_dtoa_result -#define g__fmt __gdtoa_g__fmt -#define gethex __gdtoa_gethex -#define hexdig __gdtoa_hexdig -#define hexnan __gdtoa_hexnan -#define i2b __gdtoa_i2b -#define increment __gdtoa_increment -#define lshift __gdtoa_lshift -#define match __gdtoa_match -#define mult __gdtoa_mult -#define multadd __gdtoa_multadd -#define nrv_alloc __gdtoa_nrv_alloc -#define pow5mult __gdtoa_pow5mult -#define quorem __gdtoa_quorem -#define ratio __gdtoa_ratio -#define rshift __gdtoa_rshift -#define rv_alloc __gdtoa_rv_alloc -#define s2b __gdtoa_s2b -#define set_ones __gdtoa_set_ones -#define strtoIg __gdtoa_strtoIg -#define sum __gdtoa_sum -#define tens __gdtoa_tens -#define tinytens __gdtoa_tinytens -#define tinytens __gdtoa_tinytens -#define trailz __gdtoa_trailz -#define ulp __gdtoa_ulp - -extern char *add_nanbits(char *, size_t, ULong *, int); - -hidden extern char *dtoa_result; -hidden extern const double bigtens[]; -hidden extern const double tens[]; -hidden extern const double tinytens[]; -hidden extern const unsigned char hexdig[]; -hidden extern const char *const InfName[6]; -hidden extern const char *const NanName[3]; - -Bigint *Balloc(int); -void Bfree(Bigint *); -void ULtof(ULong *, ULong *, Long, int); -void ULtod(ULong *, ULong *, Long, int); -void ULtodd(ULong *, ULong *, Long, int); -void ULtoQ(ULong *, ULong *, Long, int); -void ULtox(UShort *, ULong *, Long, int); -void ULtoxL(ULong *, ULong *, Long, int); -ULong any_on(Bigint *, int); -double b2d(Bigint *, int *); -int cmp(Bigint *, Bigint *); -void copybits(ULong *, int, Bigint *); -Bigint *d2b(double, int *, int *); -void decrement(Bigint *); -Bigint *diff(Bigint *, Bigint *); -char *g__fmt(char *, char *, char *, int, ULong, size_t); -int gethex(const char **, const FPI *, Long *, Bigint **, int); + +hidden extern char *__gdtoa_dtoa_result; +hidden extern const double __gdtoa_bigtens[]; +hidden extern const double __gdtoa_tens[]; +hidden extern const double __gdtoa_tinytens[]; +hidden extern const unsigned char __gdtoa_hexdig[]; +hidden extern const char *const __gdtoa_InfName[6]; +hidden extern const char *const __gdtoa_NanName[3]; + +Bigint *__gdtoa_Balloc(int); +Bigint *__gdtoa_d2b(double, int *, int *); +Bigint *__gdtoa_diff(Bigint *, Bigint *); +Bigint *__gdtoa_i2b(int); +Bigint *__gdtoa_increment(Bigint *); +Bigint *__gdtoa_lshift(Bigint *, int); +Bigint *__gdtoa_mult(Bigint *, Bigint *); +Bigint *__gdtoa_multadd(Bigint *, int, int); +Bigint *__gdtoa_s2b(const char *, int, int, ULong, int); +Bigint *__gdtoa_set_ones(Bigint *, int); +Bigint *__gdtoa_sum(Bigint *, Bigint *); +Bigint *__gdtoa_pow5mult(Bigint *, int); +ULong __gdtoa_any_on(Bigint *, int); +char *__gdtoa_add_nanbits(char *, size_t, ULong *, int); +char *__gdtoa_g__fmt(char *, char *, char *, int, ULong, size_t); +char *__gdtoa_nrv_alloc(char *, char **, int); +char *__gdtoa_rv_alloc(int); +double __gdtoa_b2d(Bigint *, int *); +double __gdtoa_ratio(Bigint *, Bigint *); +double __gdtoa_ulp(U *); +int __gdtoa_cmp(Bigint *, Bigint *); +int __gdtoa_gethex(const char **, const FPI *, Long *, Bigint **, int); +int __gdtoa_hexnan(const char **, const FPI *, ULong *); +int __gdtoa_match(const char **, char *); +int __gdtoa_quorem(Bigint *, Bigint *); +int __gdtoa_strtoIg(const char *, char **, const FPI *, Long *, Bigint **, + int *); +int __gdtoa_trailz(Bigint *); +void __gdtoa_Bfree(Bigint *); +void __gdtoa_ULtoQ(ULong *, ULong *, Long, int); +void __gdtoa_ULtod(ULong *, ULong *, Long, int); +void __gdtoa_ULtodd(ULong *, ULong *, Long, int); +void __gdtoa_ULtof(ULong *, ULong *, Long, int); +void __gdtoa_ULtox(UShort *, ULong *, Long, int); +void __gdtoa_ULtoxL(ULong *, ULong *, Long, int); +void __gdtoa_copybits(ULong *, int, Bigint *); +void __gdtoa_decrement(Bigint *); void __gdtoa_hexdig_init(void); -int hexnan(const char **, const FPI *, ULong *); -Bigint *i2b(int); -Bigint *increment(Bigint *); -Bigint *lshift(Bigint *, int); -int match(const char **, char *); -Bigint *mult(Bigint *, Bigint *); -Bigint *multadd(Bigint *, int, int); -char *nrv_alloc(char *, char **, int); -Bigint *pow5mult(Bigint *, int); -int quorem(Bigint *, Bigint *); -double ratio(Bigint *, Bigint *); -void rshift(Bigint *, int); -char *rv_alloc(int); -Bigint *s2b(const char *, int, int, ULong, int); -Bigint *set_ones(Bigint *, int); -int strtoIg(const char *, char **, const FPI *, Long *, Bigint **, int *); -Bigint *sum(Bigint *, Bigint *); -int trailz(Bigint *); -double ulp(U *); +void __gdtoa_rshift(Bigint *, int); forceinline int lo0bits(ULong *y) { int k; diff --git a/third_party/gdtoa/gethex.c b/third_party/gdtoa/gethex.c index 00f7020410f..aaf3db70a5f 100644 --- a/third_party/gdtoa/gethex.c +++ b/third_party/gdtoa/gethex.c @@ -34,14 +34,15 @@ /* clang-format off */ int -gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) +__gdtoa_gethex(const char **sp, const FPI *fpi, + Long *exp, Bigint **bp, int sign) { Bigint *b; const unsigned char *decpt, *s0, *s, *s1; int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret; ULong L, lostbits, *x; Long e, e1; - /**** if (!hexdig['0']) __gdtoa_hexdig_init(); ****/ + /**** if (!__gdtoa_hexdig['0']) __gdtoa_hexdig_init(); ****/ *bp = 0; havedig = 0; s0 = *(const unsigned char **)sp + 2; @@ -52,27 +53,27 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) decpt = 0; zret = 0; e = 0; - if (hexdig[*s]) + if (__gdtoa_hexdig[*s]) havedig++; else { zret = 1; if (*s != '.') goto pcheck; decpt = ++s; - if (!hexdig[*s]) + if (!__gdtoa_hexdig[*s]) goto pcheck; while(*s == '0') s++; - if (hexdig[*s]) + if (__gdtoa_hexdig[*s]) zret = 0; havedig = 1; s0 = s; } - while(hexdig[*s]) + while(__gdtoa_hexdig[*s]) s++; if (*s == '.' && !decpt) { decpt = ++s; - while(hexdig[*s]) + while(__gdtoa_hexdig[*s]) s++; }/*}*/ if (decpt) @@ -90,12 +91,12 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) case '+': s++; } - if ((n = hexdig[*s]) == 0 || n > 0x19) { + if ((n = __gdtoa_hexdig[*s]) == 0 || n > 0x19) { s = s1; break; } e1 = n - 0x10; - while((n = hexdig[*++s]) !=0 && n <= 0x19) { + while((n = __gdtoa_hexdig[*++s]) !=0 && n <= 0x19) { if (e1 & 0xf8000000) big = 1; e1 = 10*e1 + n - 0x10; @@ -123,7 +124,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) } goto retz; ret_tiny: - b = Balloc(0); + b = __gdtoa_Balloc(0); b->wds = 1; b->x[0] = 1; goto dret; @@ -145,7 +146,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) if (nbits & kmask) ++n; for(j = n, k = 0; j >>= 1; ++k); - *bp = b = Balloc(k); + *bp = b = __gdtoa_Balloc(k); b->wds = n; for(j = 0; j < n0; ++j) b->x[j] = ALL_ON; @@ -157,7 +158,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) n = s1 - s0 - 1; for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) k++; - b = Balloc(k); + b = __gdtoa_Balloc(k); x = b->x; n = 0; L = 0; @@ -169,7 +170,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) L = 0; n = 0; } - L |= (hexdig[*s1] & 0x0f) << n; + L |= (__gdtoa_hexdig[*s1] & 0x0f) << n; n += 4; } *x++ = L; @@ -180,27 +181,27 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) x = b->x; if (n > nbits) { n -= nbits; - if (any_on(b,n)) { + if (__gdtoa_any_on(b,n)) { lostbits = 1; k = n - 1; if (x[k>>kshift] & 1 << (k & kmask)) { lostbits = 2; - if (k > 0 && any_on(b,k)) + if (k > 0 && __gdtoa_any_on(b,k)) lostbits = 3; } } - rshift(b, n); + __gdtoa_rshift(b, n); e += n; } else if (n < nbits) { n = nbits - n; - b = lshift(b, n); + b = __gdtoa_lshift(b, n); e -= n; x = b->x; } if (e > fpi->emax) { ovfl: - Bfree(b); + __gdtoa_Bfree(b); ovfl1: errno = ERANGE; switch (fpi->rounding) { @@ -223,7 +224,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) if (n >= nbits) { switch (fpi->rounding) { case FPI_Round_near: - if (n == nbits && (n < 2 || lostbits || any_on(b,n-1))) + if (n == nbits && (n < 2 || lostbits || __gdtoa_any_on(b,n-1))) goto one_bit; break; case FPI_Round_up: @@ -242,7 +243,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) | STRTOG_Underflow; } } - Bfree(b); + __gdtoa_Bfree(b); retz: errno = ERANGE; return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow; @@ -251,11 +252,11 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) if (lostbits) lostbits = 1; else if (k > 0) - lostbits = any_on(b,k); + lostbits = __gdtoa_any_on(b,k); if (x[k>>kshift] & 1 << (k & kmask)) lostbits |= 2; nbits -= n; - rshift(b,n); + __gdtoa_rshift(b,n); e = fpi->emin; } if (lostbits) { @@ -276,7 +277,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) } if (up) { k = b->wds; - b = increment(b); + b = __gdtoa_increment(b); x = b->x; if (irv == STRTOG_Denormal) { if (nbits == fpi->nbits - 1 @@ -286,7 +287,7 @@ gethex( const char **sp, const FPI *fpi, Long *exp, Bigint **bp, int sign) else if (b->wds > k || ((n = nbits & kmask) !=0 && hi0bits(x[k-1]) < 32-n)) { - rshift(b,1); + __gdtoa_rshift(b,1); if (++e > fpi->emax) goto ovfl; } diff --git a/third_party/gdtoa/gmisc.c b/third_party/gdtoa/gmisc.c index 1a045c51377..0271e9c161b 100644 --- a/third_party/gdtoa/gmisc.c +++ b/third_party/gdtoa/gmisc.c @@ -33,7 +33,7 @@ /* clang-format off */ void -rshift(Bigint *b, int k) +__gdtoa_rshift(Bigint *b, int k) { ULong *x, *x1, *xe, y; int n; @@ -61,7 +61,7 @@ rshift(Bigint *b, int k) } int -trailz(Bigint *b) +__gdtoa_trailz(Bigint *b) { ULong L, *x, *xe; int n = 0; diff --git a/third_party/gdtoa/hd_init.c b/third_party/gdtoa/hd_init.c index ec3971bfefe..a25a430de25 100644 --- a/third_party/gdtoa/hd_init.c +++ b/third_party/gdtoa/hd_init.c @@ -30,45 +30,22 @@ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/gdtoa/gdtoa.internal.h" -/* clang-format off */ -#if 0 -unsigned char hexdig[256]; - -static void -htinit(unsigned char *h, unsigned char *s, int inc) -{ - int i, j; - for(i = 0; (j = s[i]) !=0; i++) - h[j] = i + inc; - } - -void -__gdtoa_hexdig_init(Void) /* Use of hexdig_init omitted 20121220 to avoid a */ - /* race condition when multiple threads are used. */ -{ -#define USC (unsigned char *) - htinit(hexdig, USC "0123456789", 0x10); - htinit(hexdig, USC "abcdef", 0x10 + 10); - htinit(hexdig, USC "ABCDEF", 0x10 + 10); - } -#else -const unsigned char hexdig[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0, - 0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,26,27,28,29,30,31,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - }; -#endif +const unsigned char __gdtoa_hexdig[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, // + 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // +}; diff --git a/third_party/gdtoa/hexnan.c b/third_party/gdtoa/hexnan.c index 0830324ba78..ecb80169f08 100644 --- a/third_party/gdtoa/hexnan.c +++ b/third_party/gdtoa/hexnan.c @@ -46,12 +46,12 @@ L_shift(ULong *x, ULong *x1, int i) } int -hexnan( const char **sp, const FPI *fpi, ULong *x0) +__gdtoa_hexnan( const char **sp, const FPI *fpi, ULong *x0) { ULong c, h, *x, *x1, *xe; const char *s; int havedig, hd0, i, nbits; - /**** if (!hexdig['0']) __gdtoa_hexdig_init(); ****/ + /**** if (!__gdtoa_hexdig['0']) __gdtoa_hexdig_init(); ****/ nbits = fpi->nbits; x = x0 + (nbits >> kshift); if (nbits & kmask) @@ -70,7 +70,7 @@ hexnan( const char **sp, const FPI *fpi, ULong *x0) && *(const unsigned char*)(s+3) > ' ') s += 2; while((c = *(const unsigned char*)++s)) { - if (!(h = hexdig[c])) { + if (!(h = __gdtoa_hexdig[c])) { if (c <= ' ') { if (hd0 < havedig) { if (x < x1 && i < 8) diff --git a/third_party/gdtoa/misc.c b/third_party/gdtoa/misc.c index 814d9a46ce0..7937dbbefa2 100644 --- a/third_party/gdtoa/misc.c +++ b/third_party/gdtoa/misc.c @@ -29,43 +29,85 @@ │ THIS SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" #include "third_party/gdtoa/gdtoa.internal.h" /* clang-format off */ static ThInfo TI0; +static void +__gdtoa_Brelease(Bigint *rv) +{ + if (!rv) return; + __gdtoa_Brelease(rv->next); + free(rv); +} + +static void +Bclear(void) +{ + int i; + for (i = 0; i < ARRAYLEN(TI0.Freelist); ++i) + __gdtoa_Brelease(TI0.Freelist[i]); + bzero(&TI0.Freelist, sizeof(TI0.Freelist)); +} + Bigint * -Balloc(int k) +__gdtoa_Balloc(int k) { +#if 0 + int x; + Bigint *rv; + x = 1 << k; + rv = (Bigint *)malloc(sizeof(Bigint) + (x-1)*sizeof(ULong)); + rv->k = k; + rv->maxwds = x; + rv->sign = 0; + rv->wds = 0; + return rv; +#else int x; Bigint *rv; - if (k <= Kmax && (rv = TI0.Freelist[k]) != 0) + static char once; + if (!once) { + atexit(Bclear); + once = 1; + } + if (k <= Kmax && (rv = TI0.Freelist[k]) != 0) { TI0.Freelist[k] = rv->next; - else { + } else { x = 1 << k; rv = (Bigint *)malloc(sizeof(Bigint) + (x-1)*sizeof(ULong)); rv->k = k; rv->maxwds = x; } - rv->sign = rv->wds = 0; + rv->sign = 0; + rv->wds = 0; return rv; +#endif } void -Bfree(Bigint *v) +__gdtoa_Bfree(Bigint *v) { +#if 0 + free(v); +#else if (v) { - if (v->k > Kmax) + if (v->k > Kmax) { free((void*)v); - else { + } else { v->next = TI0.Freelist[v->k]; TI0.Freelist[v->k] = v; } } +#endif } Bigint * -multadd(Bigint *b, int m, int a) /* multiply by m and add a */ +__gdtoa_multadd(Bigint *b, int m, int a) /* multiply by m and add a */ { int i, wds; ULong *x; @@ -83,9 +125,9 @@ multadd(Bigint *b, int m, int a) /* multiply by m and add a */ while(++i < wds); if (carry) { if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); + b1 = __gdtoa_Balloc(b->k+1); Bcopy(b1, b); - Bfree(b); + __gdtoa_Bfree(b); b = b1; } b->x[wds++] = carry; @@ -95,17 +137,17 @@ multadd(Bigint *b, int m, int a) /* multiply by m and add a */ } Bigint * -i2b(int i) +__gdtoa_i2b(int i) { Bigint *b; - b = Balloc(1); + b = __gdtoa_Balloc(1); b->x[0] = i; b->wds = 1; return b; } Bigint * -mult(Bigint *a, Bigint *b) +__gdtoa_mult(Bigint *a, Bigint *b) { Bigint *c; int k, wa, wb, wc; @@ -123,7 +165,7 @@ mult(Bigint *a, Bigint *b) wc = wa + wb; if (wc > a->maxwds) k++; - c = Balloc(k); + c = __gdtoa_Balloc(k); for(x = c->x, xa = x + wc; x < xa; x++) *x = 0; xa = a->x; @@ -150,31 +192,37 @@ mult(Bigint *a, Bigint *b) return c; } +static void +__gdtoa_pow5mult_destroy(void) { + __gdtoa_Brelease(TI0.P5s); + TI0.P5s = 0; +} + Bigint * -pow5mult(Bigint *b, int k) +__gdtoa_pow5mult(Bigint *b, int k) { Bigint *b1, *p5, *p51; int i; static const int p05[3] = { 5, 25, 125 }; - if ( (i = k & 3) !=0) - b = multadd(b, p05[i-1], 0); + if ((i = k & 3)) + b = __gdtoa_multadd(b, p05[i-1], 0); if (!(k >>= 2)) return b; - if ((p5 = TI0.P5s) == 0) { - /* first time */ - p5 = TI0.P5s = i2b(625); + if (!(p5 = TI0.P5s)) { + p5 = TI0.P5s = __gdtoa_i2b(625); p5->next = 0; + atexit(__gdtoa_pow5mult_destroy); } for(;;) { if (k & 1) { - b1 = mult(b, p5); - Bfree(b); + b1 = __gdtoa_mult(b, p5); + __gdtoa_Bfree(b); b = b1; } if (!(k >>= 1)) break; if ((p51 = p5->next) == 0) { - p51 = p5->next = mult(p5,p5); + p51 = p5->next = __gdtoa_mult(p5,p5); p51->next = 0; } p5 = p51; @@ -183,7 +231,7 @@ pow5mult(Bigint *b, int k) } Bigint * -lshift(Bigint *b, int k) +__gdtoa_lshift(Bigint *b, int k) { int i, k1, n, n1; Bigint *b1; @@ -193,7 +241,7 @@ lshift(Bigint *b, int k) n1 = n + b->wds + 1; for(i = b->maxwds; n1 > i; i <<= 1) k1++; - b1 = Balloc(k1); + b1 = __gdtoa_Balloc(k1); x1 = b1->x; for(i = 0; i < n; i++) *x1++ = 0; @@ -214,12 +262,12 @@ lshift(Bigint *b, int k) *x1++ = *x++; while(x < xe); b1->wds = n1 - 1; - Bfree(b); + __gdtoa_Bfree(b); return b1; } int -cmp(Bigint *a, Bigint *b) +__gdtoa_cmp(Bigint *a, Bigint *b) { ULong *xa, *xa0, *xb, *xb0; int i, j; @@ -227,9 +275,9 @@ cmp(Bigint *a, Bigint *b) j = b->wds; #ifdef DEBUG if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); + Bug("__gdtoa_cmp called with a->x[a->wds-1] == 0"); if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); + Bug("__gdtoa_cmp called with b->x[b->wds-1] == 0"); #endif if (i -= j) return i; @@ -247,15 +295,15 @@ cmp(Bigint *a, Bigint *b) } Bigint * -diff(Bigint *a, Bigint *b) +__gdtoa_diff(Bigint *a, Bigint *b) { Bigint *c; int i, wa, wb; ULong *xa, *xae, *xb, *xbe, *xc; ULLong borrow, y; - i = cmp(a,b); + i = __gdtoa_cmp(a,b); if (!i) { - c = Balloc(0); + c = __gdtoa_Balloc(0); c->wds = 1; c->x[0] = 0; return c; @@ -268,7 +316,7 @@ diff(Bigint *a, Bigint *b) } else i = 0; - c = Balloc(a->k); + c = __gdtoa_Balloc(a->k); c->sign = i; wa = a->wds; xa = a->x; @@ -296,7 +344,7 @@ diff(Bigint *a, Bigint *b) } double -b2d(Bigint *a, int *e) +__gdtoa_b2d(Bigint *a, int *e) { ULong *xa, *xa0, w, y, z; int k; @@ -305,7 +353,7 @@ b2d(Bigint *a, int *e) xa = xa0 + a->wds; y = *--xa; #ifdef DEBUG - if (!y) Bug("zero y in b2d"); + if (!y) Bug("zero y in __gdtoa_b2d"); #endif k = hi0bits(y); *e = 32 - k; @@ -330,7 +378,7 @@ b2d(Bigint *a, int *e) } Bigint * -d2b(double dd, int *e, int *bits) +__gdtoa_d2b(double dd, int *e, int *bits) { Bigint *b; U d; @@ -338,7 +386,7 @@ d2b(double dd, int *e, int *bits) int de, k; ULong *x, y, z; d.d = dd; - b = Balloc(1); + b = __gdtoa_Balloc(1); x = b->x; z = word0(&d) & Frac_mask; word0(&d) &= 0x7fffffff; /* clear sign bit, which we ignore */ @@ -371,13 +419,13 @@ d2b(double dd, int *e, int *bits) } const double -bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +__gdtoa_bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; const double -tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; +__gdtoa_tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; const double -tens[] = { +__gdtoa_tens[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 diff --git a/third_party/gdtoa/smisc.c b/third_party/gdtoa/smisc.c index 57227e73c4d..0b056df67ce 100644 --- a/third_party/gdtoa/smisc.c +++ b/third_party/gdtoa/smisc.c @@ -33,37 +33,37 @@ /* clang-format off */ Bigint * -s2b(const char *s, int nd0, int nd, ULong y9, int dplen) +__gdtoa_s2b(const char *s, int nd0, int nd, ULong y9, int dplen) { Bigint *b; int i, k; Long x, y; x = (nd + 8) / 9; for(k = 0, y = 1; x > y; y <<= 1, k++) ; - b = Balloc(k); + b = __gdtoa_Balloc(k); b->x[0] = y9; b->wds = 1; i = 9; if (9 < nd0) { s += 9; - do b = multadd(b, 10, *s++ - '0'); + do b = __gdtoa_multadd(b, 10, *s++ - '0'); while(++i < nd0); s += dplen; } else s += dplen + 9; for(; i < nd; i++) - b = multadd(b, 10, *s++ - '0'); + b = __gdtoa_multadd(b, 10, *s++ - '0'); return b; } double -ratio(Bigint *a, Bigint *b) +__gdtoa_ratio(Bigint *a, Bigint *b) { U da, db; int k, ka, kb; - dval(&da) = b2d(a, &ka); - dval(&db) = b2d(b, &kb); + dval(&da) = __gdtoa_b2d(a, &ka); + dval(&db) = __gdtoa_b2d(b, &kb); k = ka - kb + ULbits*(a->wds - b->wds); if (k > 0) word0(&da) += k*Exp_msk1; @@ -75,7 +75,7 @@ ratio(Bigint *a, Bigint *b) } int -match(const char **sp, char *t) +__gdtoa_match(const char **sp, char *t) { int c, d; const char *s = *sp; @@ -90,7 +90,7 @@ match(const char **sp, char *t) } void -copybits(ULong *c, int n, Bigint *b) +__gdtoa_copybits(ULong *c, int n, Bigint *b) { ULong *ce, *x, *xe; ce = c + ((n-1) >> kshift) + 1; @@ -103,7 +103,7 @@ copybits(ULong *c, int n, Bigint *b) } ULong -any_on(Bigint *b, int k) +__gdtoa_any_on(Bigint *b, int k) { int n, nwds; ULong *x, *x0, x1, x2; diff --git a/third_party/gdtoa/strtoId.c b/third_party/gdtoa/strtoId.c index 2c6cd8e9599..f1dc0312350 100644 --- a/third_party/gdtoa/strtoId.c +++ b/third_party/gdtoa/strtoId.c @@ -39,14 +39,14 @@ strtoId(const char *s, char **sp, double *f0, double *f1) Long exp[2]; Bigint *B[2]; int k, rv[2]; - B[0] = Balloc(1); + B[0] = __gdtoa_Balloc(1); B[0]->wds = 2; - k = strtoIg(s, sp, &fpi, exp, B, rv); - ULtod((ULong*)f0, B[0]->x, exp[0], rv[0]); - Bfree(B[0]); + k = __gdtoa_strtoIg(s, sp, &fpi, exp, B, rv); + __gdtoa_ULtod((ULong*)f0, B[0]->x, exp[0], rv[0]); + __gdtoa_Bfree(B[0]); if (B[1]) { - ULtod((ULong*)f1, B[1]->x, exp[1], rv[1]); - Bfree(B[1]); + __gdtoa_ULtod((ULong*)f1, B[1]->x, exp[1], rv[1]); + __gdtoa_Bfree(B[1]); } else { ((ULong*)f1)[0] = ((ULong*)f0)[0]; diff --git a/third_party/gdtoa/strtoIdd.c b/third_party/gdtoa/strtoIdd.c index 00219a88ef0..b2da7b9a7cc 100644 --- a/third_party/gdtoa/strtoIdd.c +++ b/third_party/gdtoa/strtoIdd.c @@ -39,14 +39,14 @@ strtoIdd(const char *s, char **sp, double *f0, double *f1) Long exp[2]; Bigint *B[2]; int k, rv[2]; - B[0] = Balloc(2); + B[0] = __gdtoa_Balloc(2); B[0]->wds = 4; - k = strtoIg(s, sp, &fpi, exp, B, rv); - ULtodd((ULong*)f0, B[0]->x, exp[0], rv[0]); - Bfree(B[0]); + k = __gdtoa_strtoIg(s, sp, &fpi, exp, B, rv); + __gdtoa_ULtodd((ULong*)f0, B[0]->x, exp[0], rv[0]); + __gdtoa_Bfree(B[0]); if (B[1]) { - ULtodd((ULong*)f1, B[1]->x, exp[1], rv[1]); - Bfree(B[1]); + __gdtoa_ULtodd((ULong*)f1, B[1]->x, exp[1], rv[1]); + __gdtoa_Bfree(B[1]); } else { ((ULong*)f1)[0] = ((ULong*)f0)[0]; diff --git a/third_party/gdtoa/strtoIf.c b/third_party/gdtoa/strtoIf.c index a103d27c2c0..cb3e9303fd7 100644 --- a/third_party/gdtoa/strtoIf.c +++ b/third_party/gdtoa/strtoIf.c @@ -39,14 +39,14 @@ strtoIf(const char *s, char **sp, float *f0, float *f1) Long exp[2]; Bigint *B[2]; int k, rv[2]; - B[0] = Balloc(0); + B[0] = __gdtoa_Balloc(0); B[0]->wds = 1; - k = strtoIg(s, sp, &fpi, exp, B, rv); - ULtof((ULong*)f0, B[0]->x, exp[0], rv[0]); - Bfree(B[0]); + k = __gdtoa_strtoIg(s, sp, &fpi, exp, B, rv); + __gdtoa_ULtof((ULong*)f0, B[0]->x, exp[0], rv[0]); + __gdtoa_Bfree(B[0]); if (B[1]) { - ULtof((ULong*)f1, B[1]->x, exp[1], rv[1]); - Bfree(B[1]); + __gdtoa_ULtof((ULong*)f1, B[1]->x, exp[1], rv[1]); + __gdtoa_Bfree(B[1]); } else *(ULong*)f1 = *(ULong*)f0; diff --git a/third_party/gdtoa/strtoIg.c b/third_party/gdtoa/strtoIg.c index 56654b4c6bd..621abeabb56 100644 --- a/third_party/gdtoa/strtoIg.c +++ b/third_party/gdtoa/strtoIg.c @@ -33,7 +33,7 @@ /* clang-format off */ int -strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int *rvp) +__gdtoa_strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int *rvp) { Bigint *b, *b1; int i, nb, nw, nw1, rv, rv1, swap; @@ -47,7 +47,7 @@ strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int * } e1 = exp[0]; rv1 = rv ^ STRTOG_Inexact; - b1 = Balloc(b->k); + b1 = __gdtoa_Balloc(b->k); Bcopy(b1, b); nb = fpi->nbits; nb1 = nb & 31; @@ -56,7 +56,7 @@ strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int * nw1 = nw - 1; if (rv & STRTOG_Inexlo) { swap = 0; - b1 = increment(b1); + b1 = __gdtoa_increment(b1); if ((rv & STRTOG_Retmask) == STRTOG_Zero) { if (fpi->sudden_underflow) { b1->x[0] = 0; @@ -73,7 +73,7 @@ strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int * || (nb1 && b1->x[nw1] & 1L << nb1)) { if (++e1 > fpi->emax) rv1 = STRTOG_Infinite | STRTOG_Inexhi; - rshift(b1, 1); + __gdtoa_rshift(b1, 1); } else if ((rv & STRTOG_Retmask) == STRTOG_Denormal) { if (b1->x[nw1] & 1L << nb11) { @@ -85,12 +85,12 @@ strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int * else { swap = STRTOG_Neg; if ((rv & STRTOG_Retmask) == STRTOG_Infinite) { - b1 = set_ones(b1, nb); + b1 = __gdtoa_set_ones(b1, nb); e1 = fpi->emax; rv1 = STRTOG_Normal | STRTOG_Inexlo | (rv & STRTOG_Neg); goto swapcheck; } - decrement(b1); + __gdtoa_decrement(b1); if ((rv & STRTOG_Retmask) == STRTOG_Denormal) { for(i = nw1; !b1->x[i]; --i) if (!i) { @@ -108,7 +108,7 @@ strtoIg(const char *s00, char **se, const FPI *fpi, Long *exp, Bigint **B, int * rv1 |= STRTOG_Underflow; } else { - b1 = lshift(b1, 1); + b1 = __gdtoa_lshift(b1, 1); b1->x[0] |= 1; --e1; } diff --git a/third_party/gdtoa/strtoIx.c b/third_party/gdtoa/strtoIx.c index 4ce3329ed10..c3fbaaad54a 100644 --- a/third_party/gdtoa/strtoIx.c +++ b/third_party/gdtoa/strtoIx.c @@ -40,14 +40,14 @@ strtoIx(const char *s, char **sp, void *a, void *b) Bigint *B[2]; int k, rv[2]; UShort *L = (UShort *)a, *M = (UShort *)b; - B[0] = Balloc(1); + B[0] = __gdtoa_Balloc(1); B[0]->wds = 2; - k = strtoIg(s, sp, &fpi, exp, B, rv); - ULtox(L, B[0]->x, exp[0], rv[0]); - Bfree(B[0]); + k = __gdtoa_strtoIg(s, sp, &fpi, exp, B, rv); + __gdtoa_ULtox(L, B[0]->x, exp[0], rv[0]); + __gdtoa_Bfree(B[0]); if (B[1]) { - ULtox(M, B[1]->x, exp[1], rv[1]); - Bfree(B[1]); + __gdtoa_ULtox(M, B[1]->x, exp[1], rv[1]); + __gdtoa_Bfree(B[1]); } else { M[0] = L[0]; diff --git a/third_party/gdtoa/strtod.c b/third_party/gdtoa/strtod.c index cb46eb4d6e3..9c243186d33 100644 --- a/third_party/gdtoa/strtod.c +++ b/third_party/gdtoa/strtod.c @@ -36,22 +36,21 @@ #define Avoid_Underflow #define dplen 1 -#undef tinytens -/* The factor of 2^106 in tinytens[4] helps us avoid setting the underflow */ +/* The factor of 2^106 in tiny__gdtoa_tens[4] helps us avoid setting the underflow */ /* flag unnecessarily. It leads to a song and dance at the end of strtod. */ -static const double tinytens[] = { +static const double tiny__gdtoa_tens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 9007199254740992.*9007199254740992.e-256 }; static double -sulp(U *x, int scale) +s__gdtoa_ulp(U *x, int scale) { U u; int i; double rv; - rv = ulp(x); + rv = __gdtoa_ulp(x); if (!scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0) return rv; /* Is there an example where i <= 0 ? */ word0(&u) = Exp_1 + (i << Exp_shift); @@ -108,7 +107,7 @@ strtod(const char *s00, char **se) { FPI fpi1 = fpi; fpi1.rounding = Rounding; - switch((i = gethex(&s, &fpi1, &exp, &bb, sign)) & STRTOG_Retmask) { + switch((i = __gdtoa_gethex(&s, &fpi1, &exp, &bb, sign)) & STRTOG_Retmask) { case STRTOG_NoNumber: s = s00; sign = 0; @@ -116,10 +115,10 @@ strtod(const char *s00, char **se) break; default: if (bb) { - copybits(bits, fpi.nbits, bb); - Bfree(bb); + __gdtoa_copybits(bits, fpi.nbits, bb); + __gdtoa_Bfree(bb); } - ULtod(((U*)&rv)->L, bits, exp, i); + __gdtoa_ULtod(((U*)&rv)->L, bits, exp, i); }} goto ret; } @@ -217,9 +216,9 @@ strtod(const char *s00, char **se) switch(c) { case 'i': case 'I': - if (match(&s,"nf")) { + if (__gdtoa_match(&s,"nf")) { --s; - if (!match(&s,"inity")) + if (!__gdtoa_match(&s,"inity")) ++s; word0(&rv) = 0x7ff00000; word1(&rv) = 0; @@ -228,9 +227,9 @@ strtod(const char *s00, char **se) break; case 'n': case 'N': - if (match(&s, "an")) { + if (__gdtoa_match(&s, "an")) { if (*s == '(' /*)*/ - && hexnan(&s, &fpinan, bits) + && __gdtoa_hexnan(&s, &fpinan, bits) == STRTOG_NaNbits) { word0(&rv) = 0x7ff00000 | bits[1]; word1(&rv) = bits[0]; @@ -258,7 +257,7 @@ strtod(const char *s00, char **se) k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2; dval(&rv) = y; if (k > 9) { - dval(&rv) = tens[k - 9] * dval(&rv) + z; + dval(&rv) = __gdtoa_tens[k - 9] * dval(&rv) + z; } bd0 = 0; if (nd <= DBL_DIG) { @@ -271,7 +270,7 @@ strtod(const char *s00, char **se) rv.d = -rv.d; sign = 0; } - /* rv = */ rounded_product(dval(&rv), tens[e]); + /* rv = */ rounded_product(dval(&rv), __gdtoa_tens[e]); goto ret; } i = DBL_DIG - nd; @@ -285,8 +284,8 @@ strtod(const char *s00, char **se) sign = 0; } e -= i; - dval(&rv) *= tens[i]; - /* rv = */ rounded_product(dval(&rv), tens[e]); + dval(&rv) *= __gdtoa_tens[i]; + /* rv = */ rounded_product(dval(&rv), __gdtoa_tens[e]); goto ret; } } @@ -296,7 +295,7 @@ strtod(const char *s00, char **se) rv.d = -rv.d; sign = 0; } - /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + /* rv = */ rounded_quotient(dval(&rv), __gdtoa_tens[-e]); goto ret; } } @@ -312,7 +311,7 @@ strtod(const char *s00, char **se) /* Get starting approximation = rv * 10**e1 */ if (e1 > 0) { if ( (i = e1 & 15) !=0) - dval(&rv) *= tens[i]; + dval(&rv) *= __gdtoa_tens[i]; if (e1 &= ~15) { if (e1 > DBL_MAX_10_EXP) { ovfl: @@ -329,11 +328,11 @@ strtod(const char *s00, char **se) } range_err: if (bd0) { - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); + __gdtoa_Bfree(bb); + __gdtoa_Bfree(bd); + __gdtoa_Bfree(bs); + __gdtoa_Bfree(bd0); + __gdtoa_Bfree(delta); } errno = ERANGE; goto ret; @@ -341,10 +340,10 @@ strtod(const char *s00, char **se) e1 >>= 4; for(j = 0; e1 > 1; j++, e1 >>= 1) if (e1 & 1) - dval(&rv) *= bigtens[j]; - /* The last multiplication could overflow. */ + dval(&rv) *= __gdtoa_bigtens[j]; + /* The last __gdtoa_multiplication could overflow. */ word0(&rv) -= P*Exp_msk1; - dval(&rv) *= bigtens[j]; + dval(&rv) *= __gdtoa_bigtens[j]; if ((z = word0(&rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-P)) goto ovfl; @@ -361,15 +360,15 @@ strtod(const char *s00, char **se) else if (e1 < 0) { e1 = -e1; if ( (i = e1 & 15) !=0) - dval(&rv) /= tens[i]; + dval(&rv) /= __gdtoa_tens[i]; if (e1 >>= 4) { - if (e1 >= 1 << n_bigtens) + if (e1 >= 1 << n___gdtoa_bigtens) goto undfl; if (e1 & Scale_Bit) scale = 2*P; for(j = 0; e1 > 0; j++, e1 >>= 1) if (e1 & 1) - dval(&rv) *= tinytens[j]; + dval(&rv) *= tiny__gdtoa_tens[j]; if (scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) >> Exp_shift)) > 0) { /* scaled rv is denormal; zap j low bits */ @@ -394,12 +393,12 @@ strtod(const char *s00, char **se) } /* Now the hard part -- adjusting rv to the correct value.*/ /* Put digits into bd: true value = bd * 10^e */ - bd0 = s2b(s0, nd0, nd, y, dplen); + bd0 = __gdtoa_s2b(s0, nd0, nd, y, dplen); for(;;) { - bd = Balloc(bd0->k); + bd = __gdtoa_Balloc(bd0->k); Bcopy(bd, bd0); - bb = d2b(dval(&rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ - bs = i2b(1); + bb = __gdtoa_d2b(dval(&rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = __gdtoa_i2b(1); if (e >= 0) { bb2 = bb5 = 0; bd2 = bd5 = e; @@ -440,26 +439,26 @@ strtod(const char *s00, char **se) bs2 -= i; } if (bb5 > 0) { - bs = pow5mult(bs, bb5); - bb1 = mult(bs, bb); - Bfree(bb); + bs = __gdtoa_pow5mult(bs, bb5); + bb1 = __gdtoa_mult(bs, bb); + __gdtoa_Bfree(bb); bb = bb1; } if (bb2 > 0) - bb = lshift(bb, bb2); + bb = __gdtoa_lshift(bb, bb2); if (bd5 > 0) - bd = pow5mult(bd, bd5); + bd = __gdtoa_pow5mult(bd, bd5); if (bd2 > 0) - bd = lshift(bd, bd2); + bd = __gdtoa_lshift(bd, bd2); if (bs2 > 0) - bs = lshift(bs, bs2); - delta = diff(bb, bd); + bs = __gdtoa_lshift(bs, bs2); + delta = __gdtoa_diff(bb, bd); dsign = delta->sign; delta->sign = 0; - i = cmp(delta, bs); + i = __gdtoa_cmp(delta, bs); if (Rounding != 1) { if (i < 0) { - /* Error is less than an ulp */ + /* Error is less than an __gdtoa_ulp */ if (!delta->x[0] && delta->wds <= 1) { /* exact */ break; @@ -477,8 +476,8 @@ strtod(const char *s00, char **se) y = word0(&rv) & Exp_mask; if (!scale || y > 2*P*Exp_msk1) { - delta = lshift(delta,Log2P); - if (cmp(delta, bs) <= 0) + delta = __gdtoa_lshift(delta,Log2P); + if (__gdtoa_cmp(delta, bs) <= 0) dval(&adj) = -0.5; } } @@ -486,11 +485,11 @@ strtod(const char *s00, char **se) if (scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) word0(&adj) += (2*P+1)*Exp_msk1 - y; - dval(&rv) += adj.d*ulp(&rv); + dval(&rv) += adj.d*__gdtoa_ulp(&rv); } break; } - dval(&adj) = ratio(delta, bs); + dval(&adj) = __gdtoa_ratio(delta, bs); if (adj.d < 1.) dval(&adj) = 1.; if (adj.d <= 0x7ffffffe) { @@ -504,7 +503,7 @@ strtod(const char *s00, char **se) } if (scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) word0(&adj) += (2*P+1)*Exp_msk1 - y; - dval(&adj) *= ulp(&rv); /* XXX */ + dval(&adj) *= __gdtoa_ulp(&rv); /* XXX */ if (dsign) { if (word0(&rv) == Big0 && word1(&rv) == Big1) goto ovfl; @@ -515,7 +514,7 @@ strtod(const char *s00, char **se) goto cont; } if (i < 0) { - /* Error is less than half an ulp -- check for + /* Error is less than half an __gdtoa_ulp -- check for * special case of mantissa a power of two. */ if (dsign || word1(&rv) || word0(&rv) & Bndry_mask || @@ -526,8 +525,8 @@ strtod(const char *s00, char **se) /* exact result */ break; } - delta = lshift(delta,Log2P); - if (cmp(delta, bs) > 0) + delta = __gdtoa_lshift(delta,Log2P); + if (__gdtoa_cmp(delta, bs) > 0) goto drop_down; break; } @@ -539,7 +538,7 @@ strtod(const char *s00, char **se) (scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : 0xffffffff)) { - /*boundary case -- increment exponent*/ + /*boundary case -- __gdtoa_increment exponent*/ if (word0(&rv) == Big0 && word1(&rv) == Big1) goto ovfl; word0(&rv) = (word0(&rv) & Exp_mask) @@ -552,7 +551,7 @@ strtod(const char *s00, char **se) } else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { drop_down: - /* boundary case -- decrement exponent */ + /* boundary case -- __gdtoa_decrement exponent */ if (scale) { L = word0(&rv) & Exp_mask; if (L <= (2*P+1)*Exp_msk1) { @@ -576,16 +575,16 @@ strtod(const char *s00, char **se) else if (!(word1(&rv) & Lsb)) break; if (dsign) - dval(&rv) += sulp(&rv, scale); + dval(&rv) += s__gdtoa_ulp(&rv, scale); else { - dval(&rv) -= sulp(&rv, scale); + dval(&rv) -= s__gdtoa_ulp(&rv, scale); if (!dval(&rv)) goto undfl; } dsign = 1 - dsign; break; } - if ((aadj = ratio(delta, bs)) <= 2.) { + if ((aadj = __gdtoa_ratio(delta, bs)) <= 2.) { if (dsign) aadj = dval(&aadj1) = 1.; else if (word1(&rv) || word0(&rv) & Bndry_mask) { @@ -621,7 +620,7 @@ strtod(const char *s00, char **se) if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { dval(&rv0) = dval(&rv); word0(&rv) -= P*Exp_msk1; - dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&adj) = dval(&aadj1) * __gdtoa_ulp(&rv); dval(&rv) += dval(&adj); if ((word0(&rv) & Exp_mask) >= Exp_msk1*(DBL_MAX_EXP+Bias-P)) { @@ -644,7 +643,7 @@ strtod(const char *s00, char **se) } word0(&aadj1) += (2*P+1)*Exp_msk1 - y; } - dval(&adj) = dval(&aadj1) * ulp(&rv); + dval(&adj) = dval(&aadj1) * __gdtoa_ulp(&rv); dval(&rv) += dval(&adj); } z = word0(&rv) & Exp_mask; @@ -662,16 +661,16 @@ strtod(const char *s00, char **se) break; } cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); + __gdtoa_Bfree(bb); + __gdtoa_Bfree(bd); + __gdtoa_Bfree(bs); + __gdtoa_Bfree(delta); } - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); + __gdtoa_Bfree(bb); + __gdtoa_Bfree(bd); + __gdtoa_Bfree(bs); + __gdtoa_Bfree(bd0); + __gdtoa_Bfree(delta); if (scale) { word0(&rv0) = Exp_1 - 2*P*Exp_msk1; word1(&rv0) = 0; diff --git a/third_party/gdtoa/strtodI.c b/third_party/gdtoa/strtodI.c index 48967b58f4f..8a4ae9e9702 100644 --- a/third_party/gdtoa/strtodI.c +++ b/third_party/gdtoa/strtodI.c @@ -33,11 +33,11 @@ /* clang-format off */ static double -ulpdown(U *d) +__gdtoa_ulpdown(U *d) { double u; ULong *L = d->L; - u = ulp(d); + u = __gdtoa_ulp(d); if (!(L[0] | (L[1] & 0xfffff)) && (L[1] & 0x7ff00000) > 0x00100000) u *= 0.5; @@ -77,11 +77,11 @@ strtodI(const char *s, char **sp, double *dd) } switch(j) { case STRTOG_Inexlo: - dval(&u[1]) = dval(&u[0]) + ulp(&u[0]); + dval(&u[1]) = dval(&u[0]) + __gdtoa_ulp(&u[0]); break; case STRTOG_Inexhi: dval(&u[1]) = dval(&u[0]); - dval(&u[0]) -= ulpdown(u); + dval(&u[0]) -= __gdtoa_ulpdown(u); break; default: dval(&u[1]) = dval(&u[0]); diff --git a/third_party/gdtoa/strtodg.c b/third_party/gdtoa/strtodg.c index 9165ec2aeb0..cf8bc0062f0 100644 --- a/third_party/gdtoa/strtodg.c +++ b/third_party/gdtoa/strtodg.c @@ -39,7 +39,7 @@ fivesbits[] = { 0, 3, 5, 7, 10, 12, 14, 17, 19, 21, 47, 49, 52 }; Bigint * -increment(Bigint *b) +__gdtoa_increment(Bigint *b) { ULong *x, *xe; Bigint *b1; @@ -54,9 +54,9 @@ increment(Bigint *b) } while(x < xe); { if (b->wds >= b->maxwds) { - b1 = Balloc(b->k+1); + b1 = __gdtoa_Balloc(b->k+1); Bcopy(b1,b); - Bfree(b); + __gdtoa_Bfree(b); b = b1; } b->x[b->wds++] = 1; @@ -65,7 +65,7 @@ increment(Bigint *b) } void -decrement(Bigint *b) +__gdtoa_decrement(Bigint *b) { ULong *x, *xe; x = b->x; @@ -95,14 +95,14 @@ all_on(Bigint *b, int n) } Bigint * -set_ones(Bigint *b, int n) +__gdtoa_set_ones(Bigint *b, int n) { int k; ULong *x, *xe; k = (n + ((1 << kshift) - 1)) >> kshift; if (b->k < k) { - Bfree(b); - b = Balloc(k); + __gdtoa_Bfree(b); + b = __gdtoa_Balloc(k); } k = n >> kshift; if (n &= kmask) @@ -124,7 +124,7 @@ rvOK(U *d, const FPI *fpi, Long *exp, ULong *bits, int exact, int rd, int *irv) ULong carry, inex, lostbits; int bdif, e, j, k, k1, nb, rv; carry = rv = 0; - b = d2b(dval(d), &e, &bdif); + b = __gdtoa_d2b(dval(d), &e, &bdif); bdif -= nb = fpi->nbits; e += bdif; if (bdif <= 0) { @@ -161,24 +161,24 @@ rvOK(U *d, const FPI *fpi, Long *exp, ULong *bits, int exact, int rd, int *irv) trunc: inex = lostbits = 0; if (bdif > 0) { - if ( (lostbits = any_on(b, bdif)) !=0) + if ( (lostbits = __gdtoa_any_on(b, bdif)) !=0) inex = STRTOG_Inexlo; - rshift(b, bdif); + __gdtoa_rshift(b, bdif); if (carry) { inex = STRTOG_Inexhi; - b = increment(b); + b = __gdtoa_increment(b); if ( (j = nb & kmask) !=0) j = ULbits - j; if (hi0bits(b->x[b->wds - 1]) != j) { if (!lostbits) lostbits = b->x[0] & 1; - rshift(b, 1); + __gdtoa_rshift(b, 1); e++; } } } else if (bdif < 0) - b = lshift(b, -bdif); + b = __gdtoa_lshift(b, -bdif); if (e < fpi->emin) { k = fpi->emin - e; e = fpi->emin; @@ -189,15 +189,15 @@ rvOK(U *d, const FPI *fpi, Long *exp, ULong *bits, int exact, int rd, int *irv) else { k1 = k - 1; if (k1 > 0 && !lostbits) - lostbits = any_on(b, k1); + lostbits = __gdtoa_any_on(b, k1); if (!lostbits && !exact) goto ret; lostbits |= carry = b->x[k1>>kshift] & (1 << (k1 & kmask)); - rshift(b, k); + __gdtoa_rshift(b, k); *irv = STRTOG_Denormal; if (carry) { - b = increment(b); + b = __gdtoa_increment(b); inex = STRTOG_Inexhi | STRTOG_Underflow; } else if (lostbits) @@ -211,11 +211,11 @@ rvOK(U *d, const FPI *fpi, Long *exp, ULong *bits, int exact, int rd, int *irv) b->wds = inex = 0; } *exp = e; - copybits(bits, nb, b); + __gdtoa_copybits(bits, nb, b); *irv |= inex; rv = 1; ret: - Bfree(b); + __gdtoa_Bfree(b); return rv; } @@ -276,7 +276,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) switch(s[1]) { case 'x': case 'X': - irv = gethex(&s, fpi, exp, &rvb, sign); + irv = __gdtoa_gethex(&s, fpi, exp, &rvb, sign); if (irv == STRTOG_NoNumber) { s = s00; sign = 0; @@ -376,9 +376,9 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) switch(c) { case 'i': case 'I': - if (match(&s,"nf")) { + if (__gdtoa_match(&s,"nf")) { --s; - if (!match(&s,"inity")) + if (!__gdtoa_match(&s,"inity")) ++s; irv = STRTOG_Infinite; goto infnanexp; @@ -386,11 +386,11 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) break; case 'n': case 'N': - if (match(&s, "an")) { + if (__gdtoa_match(&s, "an")) { irv = STRTOG_NaN; *exp = fpi->emax + 1; if (*s == '(') - irv = hexnan(&s, fpi, bits); + irv = __gdtoa_hexnan(&s, fpi, bits); goto infnanexp; } } @@ -421,7 +421,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) k = nd < DBL_DIG + 2 ? nd : DBL_DIG + 2; dval(&rv) = y; if (k > 9) - dval(&rv) = tens[k - 9] * dval(&rv) + z; + dval(&rv) = __gdtoa_tens[k - 9] * dval(&rv) + z; bd0 = 0; if (nbits <= P && nd <= DBL_DIG) { if (!e) { @@ -431,7 +431,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) else if (e > 0) { if (e <= Ten_pmax) { i = fivesbits[e] + mantbits(&rv) <= P; - /* rv = */ rounded_product(dval(&rv), tens[e]); + /* rv = */ rounded_product(dval(&rv), __gdtoa_tens[e]); if (rvOK(&rv, fpi, exp, bits, i, rd, &irv)) goto ret; e1 -= e; @@ -444,15 +444,15 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) */ e2 = e - i; e1 -= i; - dval(&rv) *= tens[i]; - /* rv = */ rounded_product(dval(&rv), tens[e2]); + dval(&rv) *= __gdtoa_tens[i]; + /* rv = */ rounded_product(dval(&rv), __gdtoa_tens[e2]); if (rvOK(&rv, fpi, exp, bits, 0, rd, &irv)) goto ret; e1 -= e2; } } else if (e >= -Ten_pmax) { - /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + /* rv = */ rounded_quotient(dval(&rv), __gdtoa_tens[-e]); if (rvOK(&rv, fpi, exp, bits, 0, rd, &irv)) goto ret; e1 -= e; @@ -464,51 +464,51 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) e2 = 0; if (e1 > 0) { if ( (i = e1 & 15) !=0) - dval(&rv) *= tens[i]; + dval(&rv) *= __gdtoa_tens[i]; if (e1 &= ~15) { e1 >>= 4; - while(e1 >= (1 << (n_bigtens-1))) { + while(e1 >= (1 << (n___gdtoa_bigtens-1))) { e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; word0(&rv) &= ~Exp_mask; word0(&rv) |= Bias << Exp_shift1; - dval(&rv) *= bigtens[n_bigtens-1]; - e1 -= 1 << (n_bigtens-1); + dval(&rv) *= __gdtoa_bigtens[n___gdtoa_bigtens-1]; + e1 -= 1 << (n___gdtoa_bigtens-1); } e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; word0(&rv) &= ~Exp_mask; word0(&rv) |= Bias << Exp_shift1; for(j = 0; e1 > 0; j++, e1 >>= 1) if (e1 & 1) - dval(&rv) *= bigtens[j]; + dval(&rv) *= __gdtoa_bigtens[j]; } } else if (e1 < 0) { e1 = -e1; if ( (i = e1 & 15) !=0) - dval(&rv) /= tens[i]; + dval(&rv) /= __gdtoa_tens[i]; if (e1 &= ~15) { e1 >>= 4; - while(e1 >= (1 << (n_bigtens-1))) { + while(e1 >= (1 << (n___gdtoa_bigtens-1))) { e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; word0(&rv) &= ~Exp_mask; word0(&rv) |= Bias << Exp_shift1; - dval(&rv) *= tinytens[n_bigtens-1]; - e1 -= 1 << (n_bigtens-1); + dval(&rv) *= __gdtoa_tinytens[n___gdtoa_bigtens-1]; + e1 -= 1 << (n___gdtoa_bigtens-1); } e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; word0(&rv) &= ~Exp_mask; word0(&rv) |= Bias << Exp_shift1; for(j = 0; e1 > 0; j++, e1 >>= 1) if (e1 & 1) - dval(&rv) *= tinytens[j]; + dval(&rv) *= __gdtoa_tinytens[j]; } } - rvb = d2b(dval(&rv), &rve, &rvbits); /* rv = rvb * 2^rve */ + rvb = __gdtoa_d2b(dval(&rv), &rve, &rvbits); /* rv = rvb * 2^rve */ rve += e2; if ((j = rvbits - nbits) > 0) { - rshift(rvb, j); + __gdtoa_rshift(rvb, j); rvbits = nbits; rve += j; } @@ -521,7 +521,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) denorm = 1; j = rve - emin; if (j > 0) { - rvb = lshift(rvb, j); + rvb = __gdtoa_lshift(rvb, j); rvbits += j; } else if (j < 0) { @@ -549,7 +549,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) rvb->x[0] = rvb->wds = rvbits = 1; } else - rshift(rvb, -j); + __gdtoa_rshift(rvb, -j); } rve = rve1 = emin; if (sudden_underflow && e2 + 1 < emin) @@ -557,15 +557,15 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) } /* Now the hard part -- adjusting rv to the correct value.*/ /* Put digits into bd: true value = bd * 10^e */ - bd0 = s2b(s0, nd0, nd, y, 1); + bd0 = __gdtoa_s2b(s0, nd0, nd, y, 1); for(;;) { - bd = Balloc(bd0->k); + bd = __gdtoa_Balloc(bd0->k); Bcopy(bd, bd0); - bb = Balloc(rvb->k); + bb = __gdtoa_Balloc(rvb->k); Bcopy(bb, rvb); bbbits = rvbits - bb0; bbe = rve + bb0; - bs = i2b(1); + bs = __gdtoa_i2b(1); if (e >= 0) { bb2 = bb5 = 0; bd2 = bd5 = e; @@ -594,31 +594,31 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) bs2 -= i; } if (bb5 > 0) { - bs = pow5mult(bs, bb5); - bb1 = mult(bs, bb); - Bfree(bb); + bs = __gdtoa_pow5mult(bs, bb5); + bb1 = __gdtoa_mult(bs, bb); + __gdtoa_Bfree(bb); bb = bb1; } bb2 -= bb0; if (bb2 > 0) - bb = lshift(bb, bb2); + bb = __gdtoa_lshift(bb, bb2); else if (bb2 < 0) - rshift(bb, -bb2); + __gdtoa_rshift(bb, -bb2); if (bd5 > 0) - bd = pow5mult(bd, bd5); + bd = __gdtoa_pow5mult(bd, bd5); if (bd2 > 0) - bd = lshift(bd, bd2); + bd = __gdtoa_lshift(bd, bd2); if (bs2 > 0) - bs = lshift(bs, bs2); + bs = __gdtoa_lshift(bs, bs2); asub = 1; inex = STRTOG_Inexhi; - delta = diff(bb, bd); + delta = __gdtoa_diff(bb, bd); if (delta->wds <= 1 && !delta->x[0]) break; dsign = delta->sign; delta->sign = finished = 0; L = 0; - i = cmp(delta, bs); + i = __gdtoa_cmp(delta, bs); if (rd && i <= 0) { irv = STRTOG_Normal; if ( (finished = dsign ^ (rd&1)) !=0) { @@ -637,14 +637,14 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) if (j > 1 && lo0bits(rvb->x + i) < j - 1) goto adj1; rve = rve1 - 1; - rvb = set_ones(rvb, rvbits = nbits); + rvb = __gdtoa_set_ones(rvb, rvbits = nbits); break; } irv |= dsign ? STRTOG_Inexlo : STRTOG_Inexhi; break; } if (i < 0) { - /* Error is less than half an ulp -- check for + /* Error is less than half an __gdtoa_ulp -- check for * special case of mantissa a power of two. */ irv = dsign @@ -652,8 +652,8 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) : STRTOG_Normal | STRTOG_Inexhi; if (dsign || bbbits > 1 || denorm || rve1 == emin) break; - delta = lshift(delta,1); - if (cmp(delta, bs) > 0) { + delta = __gdtoa_lshift(delta,1); + if (__gdtoa_cmp(delta, bs) > 0) { irv = STRTOG_Normal | STRTOG_Inexlo; goto drop_down; } @@ -663,7 +663,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) /* exactly half-way between */ if (dsign) { if (denorm && all_on(rvb, rvbits)) { - /*boundary case -- increment exponent*/ + /*boundary case -- __gdtoa_increment exponent*/ rvb->wds = 1; rvb->x[0] = 1; rve = emin + nbits - (rvbits = 1); @@ -676,7 +676,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) else if (bbbits == 1) { irv = STRTOG_Normal; drop_down: - /* boundary case -- decrement exponent */ + /* boundary case -- __gdtoa_decrement exponent */ if (rve1 == emin) { irv = STRTOG_Normal | STRTOG_Inexhi; if (rvb->wds == 1 && rvb->x[0] == 1) @@ -684,7 +684,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) break; } rve -= nbits; - rvb = set_ones(rvb, rvbits = nbits); + rvb = __gdtoa_set_ones(rvb, rvbits = nbits); break; } else @@ -692,7 +692,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) if ((bbbits < nbits && !denorm) || !(rvb->x[0] & 1)) break; if (dsign) { - rvb = increment(rvb); + rvb = __gdtoa_increment(rvb); j = kmask & (ULbits - (rvbits & kmask)); if (hi0bits(rvb->x[rvb->wds - 1]) != j) rvbits++; @@ -701,12 +701,12 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) else { if (bbbits == 1) goto undfl; - decrement(rvb); + __gdtoa_decrement(rvb); irv = STRTOG_Normal | STRTOG_Inexlo; } break; } - if ((dval(&adj) = ratio(delta, bs)) <= 2.) { + if ((dval(&adj) = __gdtoa_ratio(delta, bs)) <= 2.) { adj1: inex = STRTOG_Inexlo; if (dsign) { @@ -756,23 +756,23 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) } } y = rve + rvbits; - /* adj *= ulp(dval(&rv)); */ + /* adj *= __gdtoa_ulp(dval(&rv)); */ /* if (asub) rv -= adj; else rv += adj; */ if (!denorm && rvbits < nbits) { - rvb = lshift(rvb, j = nbits - rvbits); + rvb = __gdtoa_lshift(rvb, j = nbits - rvbits); rve -= j; rvbits = nbits; } - ab = d2b(dval(&adj), &abe, &abits); + ab = __gdtoa_d2b(dval(&adj), &abe, &abits); if (abe < 0) - rshift(ab, -abe); + __gdtoa_rshift(ab, -abe); else if (abe > 0) - ab = lshift(ab, abe); + ab = __gdtoa_lshift(ab, abe); rvb0 = rvb; if (asub) { /* rv -= adj; */ j = hi0bits(rvb->x[rvb->wds-1]); - rvb = diff(rvb, ab); + rvb = __gdtoa_diff(rvb, ab); k = rvb0->wds - 1; if (denorm) /* do nothing */; @@ -785,7 +785,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) denorm = 1; } else { - rvb = lshift(rvb, 1); + rvb = __gdtoa_lshift(rvb, 1); --rve; --rve1; L = finished = 0; @@ -793,7 +793,7 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) } } else { - rvb = sum(rvb, ab); + rvb = __gdtoa_sum(rvb, ab); k = rvb->wds - 1; if (k >= rvb0->wds || hi0bits(rvb->x[k]) < hi0bits(rvb0->x[k])) { @@ -802,15 +802,15 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) denorm = 0; } else { - rshift(rvb, 1); + __gdtoa_rshift(rvb, 1); rve++; rve1++; L = 0; } } } - Bfree(ab); - Bfree(rvb0); + __gdtoa_Bfree(ab); + __gdtoa_Bfree(rvb0); if (finished) break; z = rve + rvbits; @@ -829,28 +829,28 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) break; } } - bb0 = denorm ? 0 : trailz(rvb); - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); + bb0 = denorm ? 0 : __gdtoa_trailz(rvb); + __gdtoa_Bfree(bb); + __gdtoa_Bfree(bd); + __gdtoa_Bfree(bs); + __gdtoa_Bfree(delta); } if (!denorm && (j = nbits - rvbits)) { if (j > 0) - rvb = lshift(rvb, j); + rvb = __gdtoa_lshift(rvb, j); else - rshift(rvb, -j); + __gdtoa_rshift(rvb, -j); rve -= j; } *exp = rve; - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); + __gdtoa_Bfree(bb); + __gdtoa_Bfree(bd); + __gdtoa_Bfree(bs); + __gdtoa_Bfree(bd0); + __gdtoa_Bfree(delta); if (rve > fpi->emax) { huge: - Bfree(rvb); + __gdtoa_Bfree(rvb); rvb = 0; errno = ERANGE; switch(fpi->rounding & 3) { @@ -903,8 +903,8 @@ strtodg(const char *s00, char **se, const FPI *fpi, Long *exp, ULong *bits) if (sign) irv |= STRTOG_Neg; if (rvb) { - copybits(bits, nbits, rvb); - Bfree(rvb); + __gdtoa_copybits(bits, nbits, rvb); + __gdtoa_Bfree(rvb); } return irv; } diff --git a/third_party/gdtoa/strtopd.c b/third_party/gdtoa/strtopd.c index 56d2af3d597..cfdcbf1b3c4 100644 --- a/third_party/gdtoa/strtopd.c +++ b/third_party/gdtoa/strtopd.c @@ -41,6 +41,6 @@ strtopd(const char *s, char **sp, double *d) int k; #include "third_party/gdtoa/gdtoa_fltrnds.inc" k = strtodg(s, sp, fpi, &exp, bits); - ULtod((ULong*)d, bits, exp, k); + __gdtoa_ULtod((ULong*)d, bits, exp, k); return k; } diff --git a/third_party/gdtoa/strtord.c b/third_party/gdtoa/strtord.c index 02d819653c6..9bc957c0bd0 100644 --- a/third_party/gdtoa/strtord.c +++ b/third_party/gdtoa/strtord.c @@ -35,7 +35,7 @@ extern ULong __gdtoa_NanDflt_d[2]; void -ULtod(ULong *L, ULong *bits, Long exp, int k) +__gdtoa_ULtod(ULong *L, ULong *bits, Long exp, int k) { switch(k & STRTOG_Retmask) { case STRTOG_NoNumber: @@ -78,6 +78,6 @@ strtord(const char *s, char **sp, int rounding, double *d) fpi = &fpi1; } k = strtodg(s, sp, fpi, &exp, bits); - ULtod((ULong*)d, bits, exp, k); + __gdtoa_ULtod((ULong*)d, bits, exp, k); return k; } diff --git a/third_party/gdtoa/strtordd.c b/third_party/gdtoa/strtordd.c index 4a22335f7de..4292a697231 100644 --- a/third_party/gdtoa/strtordd.c +++ b/third_party/gdtoa/strtordd.c @@ -35,7 +35,7 @@ extern ULong __gdtoa_NanDflt_d[2]; void -ULtodd(ULong *L, ULong *bits, Long exp, int k) +__gdtoa_ULtodd(ULong *L, ULong *bits, Long exp, int k) { int i, j; switch(k & STRTOG_Retmask) { @@ -176,6 +176,6 @@ strtordd(const char *s, char **sp, int rounding, double *dd) fpi = &fpi1; } k = strtodg(s, sp, fpi, &exp, bits); - ULtodd((ULong*)dd, bits, exp, k); + __gdtoa_ULtodd((ULong*)dd, bits, exp, k); return k; } diff --git a/third_party/gdtoa/strtorf.c b/third_party/gdtoa/strtorf.c index 47740e446e3..fb02d25aca6 100644 --- a/third_party/gdtoa/strtorf.c +++ b/third_party/gdtoa/strtorf.c @@ -35,7 +35,7 @@ extern ULong __gdtoa_NanDflt_f[1]; void -ULtof(ULong *L, ULong *bits, Long exp, int k) +__gdtoa_ULtof(ULong *L, ULong *bits, Long exp, int k) { switch(k & STRTOG_Retmask) { case STRTOG_NoNumber: @@ -74,6 +74,6 @@ strtorf(const char *s, char **sp, int rounding, float *f) fpi = &fpi1; } k = strtodg(s, sp, fpi, &exp, bits); - ULtof((ULong*)f, bits, exp, k); + __gdtoa_ULtof((ULong*)f, bits, exp, k); return k; } diff --git a/third_party/gdtoa/strtorx.c b/third_party/gdtoa/strtorx.c index cb5459bfba8..debf10c3141 100644 --- a/third_party/gdtoa/strtorx.c +++ b/third_party/gdtoa/strtorx.c @@ -35,7 +35,7 @@ extern UShort __gdtoa_NanDflt_ldus[5]; void -ULtox(UShort *L, ULong *bits, Long exp, int k) +__gdtoa_ULtox(UShort *L, ULong *bits, Long exp, int k) { switch(k & STRTOG_Retmask) { case STRTOG_NoNumber: @@ -85,6 +85,6 @@ strtorx(const char *s, char **sp, int rounding, void *L) fpi = &fpi1; } k = strtodg(s, sp, fpi, &exp, bits); - ULtox((UShort*)L, bits, exp, k); + __gdtoa_ULtox((UShort*)L, bits, exp, k); return k; } diff --git a/third_party/gdtoa/sum.c b/third_party/gdtoa/sum.c index ded6f7e324b..b3c7e8c9df0 100644 --- a/third_party/gdtoa/sum.c +++ b/third_party/gdtoa/sum.c @@ -33,7 +33,7 @@ /* clang-format off */ Bigint * -sum(Bigint *a, Bigint *b) +__gdtoa_sum(Bigint *a, Bigint *b) { Bigint *c; ULong carry, *xc, *xa, *xb, *xe, y; @@ -41,7 +41,7 @@ sum(Bigint *a, Bigint *b) if (a->wds < b->wds) { c = b; b = a; a = c; } - c = Balloc(a->k); + c = __gdtoa_Balloc(a->k); c->wds = a->wds; carry = 0; xa = a->x; @@ -66,9 +66,9 @@ sum(Bigint *a, Bigint *b) } if (carry) { if (c->wds == c->maxwds) { - b = Balloc(c->k + 1); + b = __gdtoa_Balloc(c->k + 1); Bcopy(b, c); - Bfree(c); + __gdtoa_Bfree(c); c = b; } c->x[c->wds++] = 1; diff --git a/third_party/gdtoa/ulp.c b/third_party/gdtoa/ulp.c index 0233936666f..d4d8218eb54 100644 --- a/third_party/gdtoa/ulp.c +++ b/third_party/gdtoa/ulp.c @@ -33,7 +33,7 @@ /* clang-format off */ double -ulp(U *x) +__gdtoa_ulp(U *x) { Long L; U a; diff --git a/third_party/infozip/zip/fileio.c b/third_party/infozip/zip/fileio.c index 91b4ba92733..7e2cd63d9a7 100644 --- a/third_party/infozip/zip/fileio.c +++ b/third_party/infozip/zip/fileio.c @@ -1951,69 +1951,6 @@ ZCONST char *to; #endif /* NO_RENAME */ -#ifdef ZMEM - -/************************/ -/* Function memset() */ -/************************/ - -/* - * memset - for systems without it - * bill davidsen - March 1990 - */ - -char * -memset(buf, init, len) -register char *buf; /* buffer loc */ -register int init; /* initializer */ -register unsigned int len; /* length of the buffer */ -{ - char *start; - - start = buf; - while (len--) *(buf++) = init; - return(start); -} - - -/************************/ -/* Function memcpy() */ -/************************/ - -char * -memcpy(dst,src,len) /* v2.0f */ -register char *dst, *src; -register unsigned int len; -{ - char *start; - - start = dst; - while (len--) - *dst++ = *src++; - return(start); -} - - -/************************/ -/* Function memcmp() */ -/************************/ - -int -memcmp(b1,b2,len) /* jpd@usl.edu -- 11/16/90 */ -register char *b1, *b2; -register unsigned int len; -{ - - if (len) do { /* examine each byte (if any) */ - if (*b1++ != *b2++) - return (*((uch *)b1-1) - *((uch *)b2-1)); /* exit when miscompare */ - } while (--len); - - return(0); /* no miscompares, yield 0 result */ -} - -#endif /* ZMEM */ - /*------------------------------------------------------------------ * Split archives diff --git a/third_party/infozip/zip/zip.c b/third_party/infozip/zip/zip.c index 568012e1fda..2cee96c45b9 100644 --- a/third_party/infozip/zip/zip.c +++ b/third_party/infozip/zip/zip.c @@ -32,6 +32,8 @@ #include "libc/str/str.h" #include "libc/log/log.h" #include "libc/dce.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/sysv/consts/sig.h" #include "libc/errno.h" #ifdef VMS # include diff --git a/third_party/infozip/zip/zipcloak.c b/third_party/infozip/zip/zipcloak.c index d821408789a..6f1386a6ee8 100644 --- a/third_party/infozip/zip/zipcloak.c +++ b/third_party/infozip/zip/zipcloak.c @@ -30,6 +30,8 @@ #include "third_party/infozip/zip/ttyio.h" #include "libc/calls/calls.h" #include "libc/log/log.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/sysv/consts/sig.h" #include "libc/stdio/temp.h" #ifndef NO_STDLIB_H # include "libc/mem/mem.h" diff --git a/third_party/infozip/zip/zipnote.c b/third_party/infozip/zip/zipnote.c index 9835ccd1d0a..3c7a9ac9997 100644 --- a/third_party/infozip/zip/zipnote.c +++ b/third_party/infozip/zip/zipnote.c @@ -25,6 +25,8 @@ #include "libc/fmt/conv.h" #include "libc/alg/alg.h" #include "libc/log/log.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/sysv/consts/sig.h" #include "libc/stdio/temp.h" /* Calculate size of static line buffer used in write (-w) mode. */ @@ -71,10 +73,6 @@ void zipnoteerr(int c, ZCONST char *h); void zipnotewarn(ZCONST char *a, ZCONST char *b); #endif -#ifdef QDOS -#define exit(p1) QDOSexit() -#endif - int set_filetype(out_path) char *out_path; { diff --git a/third_party/infozip/zip/zipsplit.c b/third_party/infozip/zip/zipsplit.c index 858f06f6c31..775bc83b8cb 100644 --- a/third_party/infozip/zip/zipsplit.c +++ b/third_party/infozip/zip/zipsplit.c @@ -24,6 +24,8 @@ #include "libc/fmt/fmt.h" #include "libc/fmt/conv.h" #include "libc/alg/alg.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/sysv/consts/sig.h" #include "libc/log/log.h" #define DEFSIZ 36000L /* Default split size (change in help() too) */ @@ -39,11 +41,6 @@ # define ZPATH_SEP '.' #else #ifdef QDOS -# define ZPATH_SEP '_' -# define INDEX "zipsplit_idx" /* Name of index file */ -# define TEMPL_FMT "%%0%dld_zip" -# define TEMPL_SIZ 17 -# define exit(p1) QDOSexit() #else #ifdef VM_CMS # define INDEX "zipsplit.idx" /* Name of index file */ diff --git a/third_party/linenoise/linenoise.c b/third_party/linenoise/linenoise.c index 679ddafd9e3..2d0092d5f09 100644 --- a/third_party/linenoise/linenoise.c +++ b/third_party/linenoise/linenoise.c @@ -257,6 +257,7 @@ static struct sigaction orig_cont; static struct sigaction orig_winch; static struct termios orig_termios; static char *history[LINENOISE_MAX_HISTORY]; +static linenoiseXlatCallback *xlatCallback; static linenoiseHintsCallback *hintsCallback; static linenoiseFreeHintsCallback *freeHintsCallback; static linenoiseCompletionCallback *completionCallback; @@ -422,12 +423,19 @@ static char HasPendingInput(int fd) { return poll((struct pollfd[]){{fd, POLLIN}}, 1, 0) == 1; } +/** + * Returns UNICODE CJK Monospace Width of string. + * + * Control codes and ANSI sequences have a width of zero. We only parse + * a limited subset of ANSI here since we don't store ANSI codes in the + * linenoiseState::buf, but we do encourage CSI color codes in prompts. + */ static size_t GetMonospaceWidth(const char *p, size_t n, char *out_haswides) { int c, d; size_t i, w; struct rune r; char haswides; - enum { kAscii, kUtf8, kEsc, kCsi1, kCsi2, kSs, kNf, kStr, kStr2 } t; + enum { kAscii, kUtf8, kEsc, kCsi1, kCsi2 } t; for (haswides = r.c = r.n = t = w = i = 0; i < n; ++i) { c = p[i] & 255; switch (t) { @@ -451,87 +459,26 @@ static size_t GetMonospaceWidth(const char *p, size_t n, char *out_haswides) { r.c <<= 6; r.c |= c & 077; if (!--r.n) { - switch (r.c) { - case 033: - t = kEsc; - break; - case 0x9b: - t = kCsi1; - break; - case 0x8e: - case 0x8f: - t = kSs; - break; - case 0x90: - case 0x98: - case 0x9d: - case 0x9e: - case 0x9f: - t = kStr; - break; - default: - d = wcwidth(r.c); - d = MAX(0, d); - w += d; - haswides |= d > 1; - t = kAscii; - break; - } + d = wcwidth(r.c); + d = MAX(0, d); + w += d; + haswides |= d > 1; + t = kAscii; } } else { goto Whoopsie; } break; case kEsc: - if (0x20 <= c && c <= 0x2f) { - t = kNf; - } else if (0x30 <= c && c <= 0x3f) { - t = kAscii; - } else if (0x20 <= c && c <= 0x5F) { - switch (c) { - case '[': - t = kCsi1; - break; - case 'N': - case 'O': - t = kSs; - break; - case 'P': - case 'X': - case ']': - case '^': - case '_': - t = kStr; - break; - case '\\': - goto Whoopsie; - default: - t = kAscii; - break; - } - } else if (0x60 <= c && c <= 0x7e) { - t = kAscii; - } else if (c == 033) { - if (i == 3) t = kAscii; + if (c == '[') { + t = kCsi1; } else { t = kAscii; } break; - case kSs: - t = kAscii; - break; - case kNf: - if (0x30 <= c && c <= 0x7e) { - t = kAscii; - } else if (!(0x20 <= c && c <= 0x2f)) { - goto Whoopsie; - } - break; case kCsi1: if (0x20 <= c && c <= 0x2f) { t = kCsi2; - } else if (c == '[' && i == 3) { - /* linux function keys */ } else if (0x40 <= c && c <= 0x7e) { t = kAscii; } else if (!(0x30 <= c && c <= 0x3f)) { @@ -545,31 +492,6 @@ static size_t GetMonospaceWidth(const char *p, size_t n, char *out_haswides) { goto Whoopsie; } break; - case kStr: - switch (c) { - case '\a': - t = kAscii; - break; - case 0033: - case 0302: - t = kStr2; - break; - default: - break; - } - break; - case kStr2: - switch (c) { - case '\a': - case '\\': - case 0234: - t = kAscii; - break; - default: - t = kStr; - break; - } - break; default: assert(0); } @@ -1745,8 +1667,10 @@ static void linenoiseEditCtrlq(struct linenoiseState *l) { static ssize_t linenoiseEdit(int stdin_fd, int stdout_fd, const char *prompt, char **obuf) { ssize_t rc; + uint64_t w; size_t nread; char *p, seq[16]; + struct rune rune; struct linenoiseState l; bzero(&l, sizeof(l)); if (!(l.buf = malloc((l.buflen = 32)))) return -1; @@ -1914,6 +1838,14 @@ static ssize_t linenoiseEdit(int stdin_fd, int stdout_fd, const char *prompt, break; default: if (!iswcntrl(seq[0])) { /* only sees canonical c0 */ + if (xlatCallback) { + rune = GetUtf8(seq, nread); + w = tpenc(xlatCallback(rune.c)); + nread = 0; + do { + seq[nread++] = w; + } while ((w >>= 8)); + } linenoiseEditInsert(&l, seq, nread); } break; @@ -2200,6 +2132,13 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) { freeHintsCallback = fn; } +/** + * Sets character translation callback. + */ +void linenoiseSetXlatCallback(linenoiseXlatCallback *fn) { + xlatCallback = fn; +} + /** * Adds completion. * diff --git a/third_party/linenoise/linenoise.h b/third_party/linenoise/linenoise.h index 9de7cb0025c..3bb74076ad4 100644 --- a/third_party/linenoise/linenoise.h +++ b/third_party/linenoise/linenoise.h @@ -12,11 +12,13 @@ typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); typedef char *(linenoiseHintsCallback)(const char *, const char **, const char **); typedef void(linenoiseFreeHintsCallback)(void *); +typedef wint_t(linenoiseXlatCallback)(wint_t); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseSetHintsCallback(linenoiseHintsCallback *); void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); +void linenoiseSetXlatCallback(linenoiseXlatCallback *); char *linenoise(const char *) nodiscard; char *linenoiseRaw(const char *, int, int) nodiscard; diff --git a/third_party/lua/lua.main.c b/third_party/lua/lua.main.c index 95adf2c4e20..c3bb8cb67f5 100644 --- a/third_party/lua/lua.main.c +++ b/third_party/lua/lua.main.c @@ -12,6 +12,7 @@ #include "libc/dce.h" #include "libc/log/log.h" #include "libc/runtime/gc.internal.h" +#include "libc/runtime/stack.h" #include "libc/sysv/consts/exit.h" #include "libc/x/x.h" #include "third_party/lua/lauxlib.h" @@ -19,6 +20,8 @@ #include "third_party/lua/lua.h" #include "third_party/lua/lualib.h" +STATIC_STACK_SIZE(0x40000); + /* clang-format off */ #if !defined(LUA_PROGNAME) diff --git a/third_party/mbedtls/net_sockets.c b/third_party/mbedtls/net_sockets.c index a5c505e5529..f74a9406d22 100644 --- a/third_party/mbedtls/net_sockets.c +++ b/third_party/mbedtls/net_sockets.c @@ -16,6 +16,7 @@ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sockaddr6.h" #include "libc/dns/dns.h" #include "libc/errno.h" @@ -25,6 +26,7 @@ #include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/msg.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" diff --git a/third_party/mbedtls/test/everest_unravaged.c b/third_party/mbedtls/test/everest_unravaged.c index 3ad6cb66ffc..665eca83028 100644 --- a/third_party/mbedtls/test/everest_unravaged.c +++ b/third_party/mbedtls/test/everest_unravaged.c @@ -33,11 +33,6 @@ asm(".include \"libc/disclaimer.inc\""); * This file is part of mbed TLS (https://tls.mbed.org) */ -#ifdef memcpy -#undef memcpy -#endif -#define memcpy(x,y,z) __builtin_memcpy(x,y,z) - #define load64_le(b) READ64LE(b) #define store64_le(b, i) WRITE64LE(b, i) diff --git a/third_party/python/Include/ceval.h b/third_party/python/Include/ceval.h index 61721bdb3ab..0822e0c785d 100644 --- a/third_party/python/Include/ceval.h +++ b/third_party/python/Include/ceval.h @@ -2,6 +2,7 @@ #define Py_CEVAL_H #include "libc/bits/likely.h" #include "libc/dce.h" +#include "libc/runtime/stack.h" #include "third_party/python/Include/object.h" #include "third_party/python/Include/pyerrors.h" #include "third_party/python/Include/pystate.h" @@ -111,15 +112,14 @@ int _Py_CheckRecursiveCall(const char *); #define Py_EnterRecursiveCall(where) \ ({ \ int rc = 0; \ - const char *rsp, *bot; \ + intptr_t rsp, bot; \ if (!IsTiny()) { \ if (IsModeDbg()) { \ PyThreadState_GET()->recursion_depth++; \ rc = _Py_CheckRecursiveCall(where); \ } else { \ - rsp = __builtin_frame_address(0); \ - asm(".weak\tape_stack_vaddr\n\t" \ - "movabs\t$ape_stack_vaddr+32768,%0" : "=r"(bot)); \ + rsp = (intptr_t)__builtin_frame_address(0); \ + bot = GetStackAddr(32768); \ if (UNLIKELY(rsp < bot)) { \ PyErr_Format(PyExc_MemoryError, "Stack overflow%s", where); \ rc = -1; \ diff --git a/third_party/python/Include/ezprint.h b/third_party/python/Include/ezprint.h index d969ad1eb9c..deb349781a9 100644 --- a/third_party/python/Include/ezprint.h +++ b/third_party/python/Include/ezprint.h @@ -18,13 +18,13 @@ static void EzPrint(PyObject *x, const char *s) { t = PyObject_Type(x); r = PyObject_Repr(t); u = PyUnicode_AsUTF8String(r); - __printf("%S ", PyBytes_GET_SIZE(u), PyBytes_AS_STRING(u)); + __printf("%.*s ", PyBytes_GET_SIZE(u), PyBytes_AS_STRING(u)); Py_DECREF(u); Py_DECREF(r); Py_DECREF(t); r = PyObject_Repr(x); u = PyUnicode_AsUTF8String(r); - __printf("%S", PyBytes_GET_SIZE(u), PyBytes_AS_STRING(u)); + __printf("%.*s", PyBytes_GET_SIZE(u), PyBytes_AS_STRING(u)); Py_DECREF(u); Py_DECREF(r); } diff --git a/third_party/python/Modules/faulthandler.c b/third_party/python/Modules/faulthandler.c index 9fd141a41af..17ee7834601 100644 --- a/third_party/python/Modules/faulthandler.c +++ b/third_party/python/Modules/faulthandler.c @@ -11,6 +11,7 @@ #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/ss.h" #include "third_party/python/Include/abstract.h" #include "third_party/python/Include/boolobject.h" #include "third_party/python/Include/ceval.h" @@ -1118,7 +1119,6 @@ faulthandler_fatal_error_py(PyObject *self, PyObject *args) Py_RETURN_NONE; } -/* TODO(jart): sigaltstack */ #if defined(HAVE_SIGALTSTACK) && defined(HAVE_SIGACTION) #define FAULTHANDLER_STACK_OVERFLOW static @@ -1352,7 +1352,6 @@ int _PyFaulthandler_Init(void) { #ifdef HAVE_SIGALTSTACK int err; - /* Try to allocate an alternate stack for faulthandler() signal handler to * be able to allocate memory on the stack, even on a stack overflow. If it * fails, ignore the error. */ @@ -1378,7 +1377,6 @@ int _PyFaulthandler_Init(void) } PyThread_acquire_lock(thread.cancel_event, 1); #endif - faulthandler_handlers_init(); return faulthandler_env_options(); } diff --git a/third_party/python/Objects/obmalloc.c b/third_party/python/Objects/obmalloc.c index 99007d9ae2b..ef574d950a8 100644 --- a/third_party/python/Objects/obmalloc.c +++ b/third_party/python/Objects/obmalloc.c @@ -98,7 +98,7 @@ _PyMem_RawMalloc(void *ctx, size_t size) { #ifdef __COSMOPOLITAN__ #ifdef __FSANITIZE_ADDRESS__ - return __asan_memalign(__BIGGEST_ALIGNMENT__, size); + return __asan_memalign(16, size); #else return dlmalloc(size); #endif diff --git a/third_party/python/Python/getsig.c b/third_party/python/Python/getsig.c index a382c6e782a..f9c153134ff 100644 --- a/third_party/python/Python/getsig.c +++ b/third_party/python/Python/getsig.c @@ -6,6 +6,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" +#include "libc/calls/struct/sigaction.h" #include "third_party/python/Include/pydebug.h" #include "third_party/python/Include/pylifecycle.h" #include "third_party/python/pyconfig.h" diff --git a/third_party/python/launch.c b/third_party/python/launch.c index 1f14de850bc..3035b47b363 100644 --- a/third_party/python/launch.c +++ b/third_party/python/launch.c @@ -6,6 +6,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/log/libfatal.internal.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" @@ -93,10 +94,10 @@ ShowCrashReportHook(int err, int fd, int sig, { PyObject *str; PyFrameObject *frame; - dprintf(2, "\nCalamity Occurred w/ Python\n"); + __printf("\nCalamity Occurred w/ Python\n"); for (frame = PyEval_GetFrame(); frame; frame = frame->f_back) { str = PyUnicode_AsUTF8String(frame->f_code->co_filename); - dprintf(2, "%s:%d\n", PyBytes_AS_STRING(str), frame->f_code->co_firstlineno/* frame->f_lineno */); + __printf("%s:%d\n", PyBytes_AS_STRING(str), frame->f_code->co_firstlineno/* frame->f_lineno */); Py_DECREF(str); } } diff --git a/third_party/python/pyconfig.h b/third_party/python/pyconfig.h index 5bea67ecf28..a3f951d8c5d 100644 --- a/third_party/python/pyconfig.h +++ b/third_party/python/pyconfig.h @@ -328,6 +328,8 @@ #define HAVE_READV 1 #define HAVE_REALPATH 1 #define HAVE_RENAMEAT 1 +#define HAVE_SIGALTSTACK 1 + /* #define HAVE_MREMAP 1 */ /* #undef HAVE_PLOCK */ /* #undef HAVE_POSIX_FALLOCATE */ @@ -336,7 +338,6 @@ /* #undef HAVE_TMPNAM */ /* #undef HAVE_TMPNAM_R */ /* #undef HAVE_SETPGRP */ -/* #undef HAVE_SIGALTSTACK */ /* #undef HAVE_STATVFS */ /* #undef HAVE_STAT_TV_NSEC2 */ /* #undef HAVE_SIGPENDING */ @@ -574,7 +575,7 @@ #define HAVE_LANGINFO_H 1 #if IsModeDbg() -#define Py_DEBUG 1 +#define Py_DEBUG 1 #endif /* #define FAST_LOOPS 1 /\* froot loops *\/ */ diff --git a/third_party/python/repl.c b/third_party/python/repl.c index 48cdb957261..45aae5174cf 100644 --- a/third_party/python/repl.c +++ b/third_party/python/repl.c @@ -43,6 +43,8 @@ #include "third_party/python/Include/yoink.h" /* clang-format off */ +STATIC_STACK_SIZE(0x100000); + STATIC_YOINK("__die"); STATIC_YOINK("zip_uri_support"); diff --git a/third_party/quickjs/qjs.c b/third_party/quickjs/qjs.c index b3fab4a0bc1..3eb9c2ea018 100644 --- a/third_party/quickjs/qjs.c +++ b/third_party/quickjs/qjs.c @@ -27,6 +27,7 @@ #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/time/time.h" @@ -34,6 +35,8 @@ #include "third_party/quickjs/cutils.h" #include "third_party/quickjs/quickjs-libc.h" +STATIC_STACK_SIZE(0x80000); + asm(".ident\t\"\\n\\n\ QuickJS (MIT License)\\n\ Copyright (c) 2017-2021 Fabrice Bellard\\n\ diff --git a/third_party/quickjs/quickjs-libc.c b/third_party/quickjs/quickjs-libc.c index 14bdea2fc67..3c2ed80c740 100644 --- a/third_party/quickjs/quickjs-libc.c +++ b/third_party/quickjs/quickjs-libc.c @@ -26,8 +26,10 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/winsize.h" #include "libc/calls/termios.h" +#include "libc/calls/typedef/sighandler_t.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" diff --git a/third_party/sqlite3/shell.c b/third_party/sqlite3/shell.c index 76a670937b7..0ee8bdf6643 100644 --- a/third_party/sqlite3/shell.c +++ b/third_party/sqlite3/shell.c @@ -90,6 +90,7 @@ #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.macros.h" #include "third_party/sqlite3/sqlite3.h" @@ -158,39 +159,6 @@ typedef unsigned char u8; # define SHELL_USE_LOCAL_GETLINE 1 #endif - -#if defined(_WIN32) || defined(WIN32) -# if SQLITE_OS_WINRT -# define SQLITE_OMIT_POPEN 1 -# else -# include -# include -# define isatty(h) _isatty(h) -# ifndef access -# define access(f,m) _access((f),(m)) -# endif -# ifndef unlink -# define unlink _unlink -# endif -# ifndef strdup -# define strdup _strdup -# endif -# undef popen -# define popen _popen -# undef pclose -# define pclose _pclose -# endif -#else -/* Make sure isatty() has a prototype. */ - -#if !defined(__RTP__) && !defined(_WRS_KERNEL) -/* popen and pclose are not C89 functions and so are -** sometimes omitted from the "libc/stdio/stdio.h" header */ -#else -#define SQLITE_OMIT_POPEN 1 -#endif -#endif - #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide isatty() * thus we always assume that we have a console. That can be diff --git a/tool/build/ar.c b/tool/build/ar.c index 2b74e52f3c5..875935e6a50 100644 --- a/tool/build/ar.c +++ b/tool/build/ar.c @@ -37,6 +37,7 @@ #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/x/x.h" +#include "tool/build/lib/getargs.h" /** * @fileoverview System Five Static Archive Builder. @@ -55,7 +56,7 @@ */ struct Args { - size_t n; + size_t i, n; char **p; }; @@ -79,6 +80,26 @@ struct Header { char fmag[2]; }; +static void NewInts(struct Ints *l, size_t n) { + l->i = 0; + l->n = n; + l->p = xmalloc(n * sizeof(int)); +} + +static void NewString(struct String *s, size_t n) { + s->i = 0; + s->n = n; + s->p = xmalloc(n); +} + +static void AppendInt(struct Ints *l, int i) { + APPEND(&l->p, &l->i, &l->n, &i); +} + +static void AppendArg(struct Args *l, char *s) { + APPEND(&l->p, &l->i, &l->n, &s); +} + static void MakeHeader(struct Header *h, const char *name, int ref, int mode, int size) { size_t n; @@ -106,14 +127,21 @@ int main(int argc, char *argv[]) { char *line; char *strs; ssize_t rc; + int *offsets; size_t wrote; size_t remain; struct stat *st; uint32_t outpos; Elf64_Sym *syms; + const char *arg; struct Args args; uint64_t outsize; uint8_t *tablebuf; + struct GetArgs ga; + struct Ints modes; + struct Ints names; + struct Ints sizes; + const char *reason; struct iovec iov[7]; const char *symname; const char *outpath; @@ -122,74 +150,45 @@ int main(int argc, char *argv[]) { struct String symbols; struct String filenames; struct Header *header1, *header2; - int *offsets, *modes, *sizes, *names; int i, j, fd, err, name, outfd, tablebufsize; if (argc == 2 && !strcmp(argv[1], "-n")) exit(0); - if (!(argc > 2 && strcmp(argv[1], "rcsD") == 0)) { fprintf(stderr, "%s%s%s\n", "Usage: ", argv[0], " rcsD ARCHIVE FILE..."); return 1; } + outpath = argv[2]; bzero(&args, sizeof(args)); - for (i = 3; i < argc; ++i) { - if (argv[i][0] != '@') { - args.p = realloc(args.p, ++args.n * sizeof(*args.p)); - args.p[args.n - 1] = strdup(argv[i]); - } else { - CHECK_NOTNULL((f = fopen(argv[i] + 1, "r"))); - while ((line = chomp(xgetline(f)))) { - if (!isempty(line)) { - args.p = realloc(args.p, ++args.n * sizeof(*args.p)); - args.p[args.n - 1] = line; - } else { - free(line); - } - } - CHECK_NE(-1, fclose(f)); - } - } - st = xmalloc(sizeof(struct stat)); - symbols.i = 0; - symbols.n = 4096; - symbols.p = xmalloc(symbols.n); - filenames.i = 0; - filenames.n = 1024; - filenames.p = xmalloc(filenames.n); - symnames.i = 0; - symnames.n = 1024; - symnames.p = xmalloc(symnames.n * sizeof(int)); - - outpath = argv[2]; - modes = xmalloc(sizeof(int) * args.n); - names = xmalloc(sizeof(int) * args.n); - sizes = xmalloc(sizeof(int) * args.n); + NewInts(&modes, 128); + NewInts(&names, 128); + NewInts(&sizes, 128); + NewInts(&symnames, 1024); + NewString(&symbols, 4096); + NewString(&filenames, 1024); + getargs_init(&ga, argv + 3); // load global symbols and populate page cache - for (i = 0; i < args.n; ++i) { + for (i = 0;; ++i) { TryAgain: - CHECK_NE(-1, (fd = open(args.p[i], O_RDONLY)), "%s", args.p[i]); + if (!(arg = getargs_next(&ga))) break; + CHECK_NE(-1, (fd = open(arg, O_RDONLY)), "%s", arg); CHECK_NE(-1, fstat(fd, st)); CHECK_LT(st->st_size, 0x7ffff000); - if (!st->st_size || S_ISDIR(st->st_mode) || endswith(args.p[i], ".pkg")) { - close(fd); - for (j = i; j + 1 < args.n; ++j) { - args.p[j] = args.p[j + 1]; - } - --args.n; + if (!st->st_size || S_ISDIR(st->st_mode) || endswith(arg, ".pkg")) { goto TryAgain; } - names[i] = filenames.i; - sizes[i] = st->st_size; - modes[i] = st->st_mode; - CONCAT(&filenames.p, &filenames.i, &filenames.n, basename(args.p[i]), - strlen(basename(args.p[i]))); + AppendArg(&args, xstrdup(arg)); + AppendInt(&names, filenames.i); + AppendInt(&sizes, st->st_size); + AppendInt(&modes, st->st_mode); + CONCAT(&filenames.p, &filenames.i, &filenames.n, basename(arg), + strlen(basename(arg))); CONCAT(&filenames.p, &filenames.i, &filenames.n, "/\n", 2); CHECK_NE(MAP_FAILED, - (elf = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0))); - CHECK(IsElf64Binary(elf, st->st_size), "%s", args.p[i]); + (elf = mmap(0, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0))); + CHECK(IsElf64Binary(elf, st->st_size), "%s", arg); CHECK_NOTNULL((strs = GetElfStringTable(elf, st->st_size))); CHECK_NOTNULL((syms = GetElfSymbolTable(elf, st->st_size, &symcount))); for (j = 0; j < symcount; ++j) { @@ -198,7 +197,7 @@ int main(int argc, char *argv[]) { if (ELF64_ST_BIND(syms[j].st_info) == STB_LOCAL) continue; symname = GetElfString(elf, st->st_size, strs, syms[j].st_name); CONCAT(&symbols.p, &symbols.i, &symbols.n, symname, strlen(symname) + 1); - APPEND(&symnames.p, &symnames.i, &symnames.n, &i); + AppendInt(&symnames, i); } CHECK_NE(-1, munmap(elf, st->st_size)); close(fd); @@ -209,7 +208,7 @@ int main(int argc, char *argv[]) { outsize = 0; tablebufsize = 4 + symnames.i * 4; tablebuf = xmalloc(tablebufsize); - offsets = xmalloc(args.n * 4); + offsets = xmalloc(args.i * 4); header1 = xmalloc(sizeof(struct Header)); header2 = xmalloc(sizeof(struct Header)); iov[0].iov_base = "!\n"; @@ -226,11 +225,11 @@ int main(int argc, char *argv[]) { outsize += (iov[5].iov_len = 60); iov[6].iov_base = filenames.p; outsize += (iov[6].iov_len = filenames.i); - for (i = 0; i < args.n; ++i) { + for (i = 0; i < args.i; ++i) { outsize += outsize & 1; offsets[i] = outsize; outsize += 60; - outsize += sizes[i]; + outsize += sizes.p[i]; } CHECK_LE(outsize, 0x7ffff000); @@ -245,40 +244,60 @@ int main(int argc, char *argv[]) { // write output archive CHECK_NE(-1, (outfd = open(outpath, O_WRONLY | O_TRUNC | O_CREAT, 0644))); ftruncate(outfd, outsize); - if ((outsize = writev(outfd, iov, ARRAYLEN(iov))) == -1) goto fail; - for (i = 0; i < args.n; ++i) { - if ((fd = open(args.p[i], O_RDONLY)) == -1) goto fail; + if ((outsize = writev(outfd, iov, ARRAYLEN(iov))) == -1) { + reason = "writev1 failed"; + goto fail; + } + for (i = 0; i < args.i; ++i) { + if ((fd = open(args.p[i], O_RDONLY)) == -1) { + reason = "open failed"; + goto fail; + } iov[0].iov_base = "\n"; outsize += (iov[0].iov_len = outsize & 1); iov[1].iov_base = header1; outsize += (iov[1].iov_len = 60); - MakeHeader(header1, "/", names[i], modes[i], sizes[i]); - if (writev(outfd, iov, 2) == -1) goto fail; - outsize += (remain = sizes[i]); - if (copy_file_range(fd, NULL, outfd, NULL, remain, 0) != remain) goto fail; + MakeHeader(header1, "/", names.p[i], modes.p[i], sizes.p[i]); + if (writev(outfd, iov, 2) == -1) { + reason = "writev2 failed"; + goto fail; + } + outsize += (remain = sizes.p[i]); + while ((rc = copy_file_range(fd, 0, outfd, 0, remain, 0)) < remain) { + if (rc <= 0) { + reason = "copy_file_range failed"; + goto fail; + } else { + remain -= rc; + } + } close(fd); } close(outfd); - for (i = 0; i < args.n; ++i) free(args.p[i]); - free(args.p); - free(header2); - free(header1); - free(offsets); - free(tablebuf); - free(sizes); - free(names); - free(modes); - free(symbols.p); + for (i = 0; i < args.i; ++i) free(args.p[i]); + getargs_destroy(&ga); free(filenames.p); free(symnames.p); + free(symbols.p); + free(tablebuf); + free(modes.p); + free(names.p); + free(sizes.p); + free(offsets); + free(header1); + free(header2); + free(args.p); free(st); return 0; fail: err = errno; - if (!err) err = 1; unlink(outpath); - fputs("error: ar failed\n", stderr); - return err; + fputs("error: ar failed: ", stderr); + fputs(reason, stderr); + fputs(": ", stderr); + fputs(strerror(err), stderr); + fputs("\n", stderr); + return 1; } diff --git a/tool/build/blinkenlights.c b/tool/build/blinkenlights.c index 6072c702da7..a063fb48ded 100644 --- a/tool/build/blinkenlights.c +++ b/tool/build/blinkenlights.c @@ -1410,7 +1410,7 @@ static void DrawMemoryUnzoomed(struct Panel *p, struct MemoryView *view, x = 129; /* PURPLE: shadow corruption */ } else if (sc == kAsanHeapFree) { x = 20; /* BLUE: heap freed */ - } else if (sc == kAsanRelocated) { + } else if (sc == kAsanHeapRelocated) { x = 16; /* BLACK: heap relocated */ } else if (sc == kAsanHeapUnderrun || sc == kAsanAllocaUnderrun) { x = 53; /* RED+PURPLETINGE: heap underrun */ diff --git a/tool/build/compile.c b/tool/build/compile.c index 5553ef58695..de1199f5616 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -482,7 +482,7 @@ void SetMemLimit(long n) { struct rlimit rlim = {n, n}; if (n <= 0) return; if (IsWindows() || IsXnu()) return; - setrlimit(!IsOpenbsd() ? RLIMIT_AS : RLIMIT_DATA, &rlim); + setrlimit(RLIMIT_AS, &rlim); } int Launch(void) { diff --git a/tool/build/lib/getargs.c b/tool/build/lib/getargs.c new file mode 100644 index 00000000000..917e9ce0446 --- /dev/null +++ b/tool/build/lib/getargs.c @@ -0,0 +1,171 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/errno.h" +#include "libc/log/log.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/prot.h" +#include "libc/sysv/errfuns.h" +#include "libc/x/x.h" +#include "tool/build/lib/getargs.h" + +/** + * @fileoverview Fast Command Line Argument Ingestion. + * + * The purpose of this library is to be able to have build commands with + * huge argument lists. The way we do that is by replacing commands like + * + * foo.com lots of args + * + * with this + * + * echo of args >args + * foo.com lots @args + * + * This iterator abstracts the process of reading the special `@` + * prefixed args. In order to do that quickly and easily, we make the + * following assumptions: + * + * 1. Arguments don't have whitespace. + * 2. Files have a trailing whitespace. + * + * We need (1) so GNU Make can go faster. Assume we tokenized based on + * newlines. We would would write that in our Makefile as follows: + * + * # don't do this + * target: thousands of args + * $(file >$@.args) $(foreach x,$^,$(file >>$@.args,$(x))) + * tool.com -o $@ @$@.args + * + * That is slow because it needs to open and close the args file + * thousands of times. If we trade away filenames with spaces then the + * following will only require a couple system calls: + * + * # do this + * target: thousands of args + * $(file >$@.args,$^) + * tool.com -o $@ @$@.args + * + * We need (2) because it make the code in this file simpler and avoids + * a malloc() dependency. Having that trailing character means argument + * parsing from files can be a zero-copy operation. + */ + +#define IsSpace(c) ((255 & (c)) <= ' ') + +/** + * Zeroes GetArgs object and sets its fields. + * @param args is borrowed for the lifetime of the GetArgs object + */ +void getargs_init(struct GetArgs *ga, char **args) { + assert(args); + bzero(ga, sizeof(*ga)); + ga->args = args; +} + +/** + * Releases memory associated with GetArgs object and zeroes it. + */ +void getargs_destroy(struct GetArgs *ga) { + if (ga->map) munmap(ga->map, ga->mapsize); + bzero(ga, sizeof(*ga)); +} + +/** + * Gets next argument, e.g. + * + * const char *s; + * while ((s = getargs_next(&ga))) { + * printf("%s\n", s); + * } + * + * @return NUL-terminated string; it should not be freed; it should be + * assumed that it stays in scope until the next getargs_next call + */ +const char *getargs_next(struct GetArgs *ga) { + int fd; + char *p; + size_t k; + unsigned m; + struct stat st; + do { + if (ga->map) { + for (; ga->j < ga->mapsize; ++ga->j) { + if (!IsSpace(ga->map[ga->j])) { + break; + } + } + k = 0; +#if defined(__x86__) && defined(__GNUC__) && !defined(__STRICT_ANSI__) + typedef unsigned char xmm_t + __attribute__((__vector_size__(16), __aligned__(1))); + for (; ga->j + k + 16 <= ga->mapsize; k += 16) { + if ((m = __builtin_ia32_pmovmskb128( + *(const xmm_t *)(ga->map + ga->j + k) > + (xmm_t){' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' '}) ^ + 0xffff)) { + k += __builtin_ctzl(m); + break; + } + } +#endif + for (; ga->j + k < ga->mapsize; ++k) { + if (IsSpace(ga->map[ga->j + k])) { + break; + } + } + if (k) { + if (ga->j + k < ga->mapsize) { + ga->map[ga->j + k] = 0; + p = ga->map + ga->j; + ga->j += ++k; + return p; + } else { + eio(); + break; + } + } + if (munmap(ga->map, ga->mapsize) == -1) break; + ga->map = 0; + ga->mapsize = 0; + ga->j = 0; + } + if (!(p = ga->args[ga->i])) return 0; + ++ga->i; + if (*p != '@') return p; + ++p; + if ((fd = open((ga->path = p), O_RDONLY)) != -1) { + fstat(fd, &st); + if ((p = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, + 0)) != MAP_FAILED) { + ga->map = p; + ga->mapsize = st.st_size; + } + close(fd); + } + } while (ga->map); + perror(ga->path); + exit(1); +} diff --git a/tool/build/lib/getargs.h b/tool/build/lib/getargs.h new file mode 100644 index 00000000000..d1a00518cb1 --- /dev/null +++ b/tool/build/lib/getargs.h @@ -0,0 +1,20 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_GETARGS_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_GETARGS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct GetArgs { + size_t i, j; + char **args; + char *path; + char *map; + size_t mapsize; +}; + +void getargs_init(struct GetArgs *, char **); +const char *getargs_next(struct GetArgs *); +void getargs_destroy(struct GetArgs *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_GETARGS_H_ */ diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index d2dc989886f..76d9c87c756 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -60,6 +60,7 @@ #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" +#include "libc/sysv/consts/ss.h" #include "libc/sysv/consts/tcp.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/consts/w.h" diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index d4f68478238..0cf8faff928 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -45,6 +45,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/x/x.h" #include "third_party/getopt/getopt.h" +#include "tool/build/lib/getargs.h" #define MAX_READ FRAMESIZE @@ -223,59 +224,51 @@ wontreturn void OnMissingFile(const char *list, const char *src) { * notation on mkdeps related rules, the build will * automatically restart itself. */ - fprintf(stderr, "%s %s...\n", "Refreshing", list); - unlink(list); + if (list) { + fprintf(stderr, "%s %s...\n", "Refreshing", list); + unlink(list); + } exit(1); } void LoadRelationships(int argc, char *argv[]) { int fd; + char *buf; ssize_t rc; bool skipme; - FILE *finpaths; struct Edge edge; - char *line, *buf; + struct GetArgs ga; unsigned srcid, dependency; - size_t i, linecap, inclen, size; + size_t i, inclen, size; const char *p, *pe, *src, *path, *pathend; - line = NULL; - linecap = 0; inclen = strlen(kIncludePrefix); buf = gc(xmemalign(PAGESIZE, PAGESIZE + MAX_READ + 16)); buf += PAGESIZE; buf[-1] = '\n'; - for (i = optind; i < argc; ++i) { - if (!(finpaths = fopen(argv[i], "r"))) { - fprintf(stderr, "\n\e[1mERROR: %s FAILED BECAUSE %s CAUSED %m\e[0m\n\n", - argv[0], argv[i]); - exit(1); - } - while (getline(&line, &linecap, finpaths) != -1) { - src = chomp(line); - if (ShouldSkipSource(src)) continue; - srcid = GetSourceId(src, strlen(src)); - if ((fd = open(src, O_RDONLY)) == -1) OnMissingFile(argv[i], src); - CHECK_NE(-1, (rc = read(fd, buf, MAX_READ))); - close(fd); - size = rc; - bzero(buf + size, 16); - for (p = buf, pe = p + size; p < pe; ++p) { - p = strstr(p, kIncludePrefix); - if (!p) break; - path = p + inclen; - pathend = memchr(path, '"', pe - path); - if (pathend && (p[-1] == '#' || p[-1] == '.') && p[-2] == '\n') { - dependency = GetSourceId(path, pathend - path); - edge.from = srcid; - edge.to = dependency; - append(&edges, &edge); - p = pathend; - } + getargs_init(&ga, argv + optind); + while ((src = getargs_next(&ga))) { + if (ShouldSkipSource(src)) continue; + srcid = GetSourceId(src, strlen(src)); + if ((fd = open(src, O_RDONLY)) == -1) OnMissingFile(ga.path, src); + CHECK_NE(-1, (rc = read(fd, buf, MAX_READ))); + close(fd); + size = rc; + bzero(buf + size, 16); + for (p = buf, pe = p + size; p < pe; ++p) { + p = strstr(p, kIncludePrefix); + if (!p) break; + path = p + inclen; + pathend = memchr(path, '"', pe - path); + if (pathend && (p[-1] == '#' || p[-1] == '.') && p[-2] == '\n') { + dependency = GetSourceId(path, pathend - path); + edge.from = srcid; + edge.to = dependency; + append(&edges, &edge); + p = pathend; } } - CHECK_NE(-1, fclose(finpaths)); } - free(line); + getargs_destroy(&ga); } void GetOpts(int argc, char *argv[]) { diff --git a/tool/build/rollup.c b/tool/build/rollup.c index 75d47a465b9..c19fbfe9aca 100644 --- a/tool/build/rollup.c +++ b/tool/build/rollup.c @@ -30,6 +30,7 @@ #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "tool/build/lib/getargs.h" #define LOOKINGAT(p, pe, s) LookingAt(p, pe, s, strlen(s)) #define APPENDSTR(s) AppendData(s, strlen(s)) @@ -137,7 +138,8 @@ static void Visit(const char *path) { } int main(int argc, char *argv[]) { - int i; + const char *src; + struct GetArgs ga; APPENDSTR("#ifndef COSMOPOLITAN_H_\n"); APPENDSTR("#define COSMOPOLITAN_H_\n"); /* APPENDSTR("#define IMAGE_BASE_VIRTUAL "); */ @@ -146,9 +148,11 @@ int main(int argc, char *argv[]) { /* APPENDSTR("#define IMAGE_BASE_PHYSICAL "); */ /* AppendInt(IMAGE_BASE_PHYSICAL); */ /* APPENDSTR("\n"); */ - for (i = 1; i < argc; ++i) { - Visit(argv[i]); + getargs_init(&ga, argv + 1); + while ((src = getargs_next(&ga))) { + Visit(src); } + getargs_destroy(&ga); APPENDSTR("\n"); APPENDSTR("#endif /* COSMOPOLITAN_H_ */\n"); CHECK_EQ(output.i, write(1, output.p, output.i)); diff --git a/tool/emacs/cosmo-c-builtins.el b/tool/emacs/cosmo-c-builtins.el index 0671eb1129e..862b8d7badb 100644 --- a/tool/emacs/cosmo-c-builtins.el +++ b/tool/emacs/cosmo-c-builtins.el @@ -1293,6 +1293,9 @@ "YOINK" "STATIC_YOINK" "STATIC_YOINK_SOURCE" + "STATIC_STACK_ADDR" + "STATIC_STACK_SIZE" + "STATIC_SYMBOL" "PYTHON_YOINK" "PYTHON_PROVIDE" "STRINGIFY")) diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 5b809171df5..eca89f3c7ac 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -109,10 +109,18 @@ (let ((case-fold-search nil)) (xref-find-references (format "%S" (symbol-at-point))))) +(defun cosmo-symbol-at-point (&optional no-properties) + (let ((stab (copy-syntax-table))) + (with-syntax-table stab + (modify-syntax-entry ?+ " ") + (let ((thing (thing-at-point 'symbol no-properties))) + (when thing + (intern thing)))))) + (defun cosmo-xref-find-definitions () (interactive) (let ((case-fold-search nil)) - (xref-find-definitions (format "%S" (symbol-at-point))))) + (xref-find-definitions (format "%S" (cosmo-symbol-at-point))))) (global-set-key (kbd "M-,") 'xref-pop-marker-stack) (global-set-key (kbd "M-.") 'cosmo-xref-find-definitions) diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 29340454e66..9b7975188ff 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -51,6 +51,7 @@ #include "libc/runtime/directmap.internal.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/stdio/append.internal.h" @@ -125,6 +126,8 @@ #include "tool/build/lib/case.h" #include "tool/build/lib/psk.h" +STATIC_STACK_SIZE(0x40000); + /** * @fileoverview redbean - single-file distributable web server * @@ -2601,8 +2604,7 @@ static char *BadMethod(void) { } static int GetDecimalWidth(long x) { - int w = x ? ceil(log10(x)) : 1; - return w + (w - 1) / 3; + return LengthInt64Thousands(x); } static int GetOctalWidth(int x) { @@ -5425,7 +5427,7 @@ static const luaL_Reg kLuaLibs[] = { {"re", LuaRe}, // {"lsqlite3", luaopen_lsqlite3}, // #ifndef UNSECURE - {"argon2", luaopen_argon2}, // + {"argon2", luaopen_argon2}, // #endif }; diff --git a/tool/viz/printimage.c b/tool/viz/printimage.c index 9849a88c49b..270fdb66418 100644 --- a/tool/viz/printimage.c +++ b/tool/viz/printimage.c @@ -390,7 +390,7 @@ void WithImageFile(const char *path, sxn = xn; dyn = g_flags.height; dxn = g_flags.width; -#if 1 +#if 0 while (HALF(syn) > dyn || HALF(sxn) > dxn) { if (HALF(sxn) > dxn) { Magikarp2xX(yn, xn, data, syn, sxn);