diff options
Diffstat (limited to 'tinycc/lib')
| -rw-r--r-- | tinycc/lib/Makefile | 94 | ||||
| -rw-r--r-- | tinycc/lib/alloca-bt.S | 96 | ||||
| -rw-r--r-- | tinycc/lib/alloca.S | 85 | ||||
| -rw-r--r-- | tinycc/lib/armeabi.c | 544 | ||||
| -rw-r--r-- | tinycc/lib/armflush.c | 51 | ||||
| -rw-r--r-- | tinycc/lib/atomic.S | 858 | ||||
| -rw-r--r-- | tinycc/lib/bcheck.c | 2270 | ||||
| -rw-r--r-- | tinycc/lib/bt-dll.c | 74 | ||||
| -rw-r--r-- | tinycc/lib/bt-exe.c | 74 | ||||
| -rw-r--r-- | tinycc/lib/bt-log.c | 47 | ||||
| -rw-r--r-- | tinycc/lib/builtin.c | 164 | ||||
| -rw-r--r-- | tinycc/lib/dsohandle.c | 1 | ||||
| -rw-r--r-- | tinycc/lib/lib-arm64.c | 677 | ||||
| -rw-r--r-- | tinycc/lib/libtcc1.c | 641 | ||||
| -rw-r--r-- | tinycc/lib/stdatomic.c | 166 | ||||
| -rw-r--r-- | tinycc/lib/tcov.c | 428 | ||||
| -rw-r--r-- | tinycc/lib/va_list.c | 67 |
17 files changed, 6337 insertions, 0 deletions
diff --git a/tinycc/lib/Makefile b/tinycc/lib/Makefile new file mode 100644 index 0000000..81e390b --- /dev/null +++ b/tinycc/lib/Makefile @@ -0,0 +1,94 @@ +# +# Tiny C Compiler Makefile for libtcc1.a +# + +TOP = .. +include $(TOP)/Makefile +VPATH = $(TOPSRC)/lib $(TOPSRC)/win32/lib +T = $(or $(CROSS_TARGET),$(NATIVE_TARGET),unknown) +X = $(if $(CROSS_TARGET),$(CROSS_TARGET)-) + +XTCC ?= $(TOP)/$(X)tcc$(EXESUF) +XCC = $(XTCC) +XAR = $(XTCC) -ar +XFLAGS-unx = -B$(TOPSRC) +XFLAGS-win = -B$(TOPSRC)/win32 -I$(TOPSRC)/include +XFLAGS = $(XFLAGS$(XCFG)) -I$(TOP) +XCFG = $(or $(findstring -win,$T),-unx) +S = $(if $(findstring yes,$(SILENT)),@$(info * $@)) + +# in order to use gcc, type: make <target>-libtcc1-usegcc=yes +arm-libtcc1-usegcc ?= no + +# This makes bounds checking 40%..60% faster. +#x86_64-libtcc1-usegcc=yes +#i386-libtcc1-usegcc=yes + +ifeq "$($(T)-libtcc1-usegcc)" "yes" + XCC = $(CC) + XAR = $(AR) + XFLAGS = $(CFLAGS) -fPIC -gdwarf -fno-omit-frame-pointer -Wno-unused-function -Wno-unused-variable +endif + +ifneq ($(CONFIG_backtrace),no) +# only for native compiler +ifneq ($(CONFIG_bcheck),no) +$(X)BCHECK_O = bcheck.o +endif +$(X)BT_O = bt-exe.o bt-log.o +$(X)B_O = $(BCHECK_O) bt-exe.o bt-log.o bt-dll.o +endif +$(X)BT_O += tcov.o + +DSO_O = dsohandle.o + +I386_O = libtcc1.o alloca.o alloca-bt.o stdatomic.o atomic.o builtin.o $(BT_O) +X86_64_O = libtcc1.o alloca.o alloca-bt.o stdatomic.o atomic.o builtin.o $(BT_O) +ARM_O = libtcc1.o armeabi.o alloca.o armflush.o stdatomic.o atomic.o builtin.o $(BT_O) +ARM64_O = lib-arm64.o stdatomic.o atomic.o builtin.o $(BT_O) +RISCV64_O = lib-arm64.o stdatomic.o atomic.o builtin.o $(BT_O) +WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o + +OBJ-i386 = $(I386_O) $(BCHECK_O) $(DSO_O) +OBJ-x86_64 = $(X86_64_O) va_list.o $(BCHECK_O) $(DSO_O) +OBJ-x86_64-osx = $(X86_64_O) va_list.o $(BCHECK_O) +OBJ-i386-win32 = $(I386_O) chkstk.o $(B_O) $(WIN_O) +OBJ-x86_64-win32 = $(X86_64_O) chkstk.o $(B_O) $(WIN_O) +OBJ-arm64 = $(ARM64_O) $(BCHECK_O) $(DSO_O) +OBJ-arm64-osx = $(ARM64_O) $(BCHECK_O) +OBJ-arm = $(ARM_O) $(BCHECK_O) $(DSO_O) +OBJ-arm-fpa = $(ARM_O) $(DSO_O) +OBJ-arm-fpa-ld = $(ARM_O) $(DSO_O) +OBJ-arm-vfp = $(ARM_O) $(DSO_O) +OBJ-arm-eabi = $(ARM_O) $(DSO_O) +OBJ-arm-eabihf = $(ARM_O) $(DSO_O) +OBJ-arm-wince = $(ARM_O) $(WIN_O) +OBJ-riscv64 = $(RISCV64_O) $(BCHECK_O) $(DSO_O) + +OBJ-extra = $(filter $(B_O),$(OBJ-$T)) +OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T))) + +ALL = $(addprefix $(TOP)/,$(X)libtcc1.a $(OBJ-extra)) + +all: $(ALL) + +$(TOP)/$(X)libtcc1.a : $(OBJ-libtcc1) + $S$(XAR) rcs $@ $^ + +$(X)%.o : %.c + $S$(XCC) -c $< -o $@ $(XFLAGS) + +$(X)%.o : %.S + $S$(XCC) -c $< -o $@ $(XFLAGS) + +$(TOP)/%.o : %.c + $S$(XCC) -c $< -o $@ $(XFLAGS) + +$(TOP)/bcheck.o : XFLAGS += -g $(if $(CONFIG_musl),-DTCC_MUSL) +$(TOP)/bt-exe.o : $(TOP)/tccrun.c + +$(X)crt1w.o : crt1.c +$(X)wincrt1w.o : wincrt1.c + +clean : + rm -f *.a *.o $(ALL) diff --git a/tinycc/lib/alloca-bt.S b/tinycc/lib/alloca-bt.S new file mode 100644 index 0000000..c161488 --- /dev/null +++ b/tinycc/lib/alloca-bt.S @@ -0,0 +1,96 @@ +/* ---------------------------------------------- */ +/* alloca-bt.S */ + +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif + +/* ---------------------------------------------- */ +#if defined __i386__ + +.globl _(__bound_alloca) +_(__bound_alloca): + pop %edx + pop %eax + mov %eax, %ecx + add $3+1,%eax + and $-4,%eax + jz p6 + +#ifdef _WIN32 +p4: + cmp $4096,%eax + jbe p5 + test %eax,-4096(%esp) + sub $4096,%esp + sub $4096,%eax + jmp p4 + +p5: +#endif + + sub %eax,%esp + mov %esp,%eax + + push %edx + push %eax + push %ecx + push %eax + call _(__bound_new_region) + add $8, %esp + pop %eax + pop %edx + +p6: + push %edx + push %edx + ret + +/* ---------------------------------------------- */ +#elif defined __x86_64__ + +.globl _(__bound_alloca) +_(__bound_alloca): +#ifdef _WIN32 + inc %rcx # add one extra to separate regions + jmp _(alloca) +.globl _(__bound_alloca_nr) +_(__bound_alloca_nr): + dec %rcx + push %rax + mov %rcx,%rdx + mov %rax,%rcx + sub $32,%rsp + call _(__bound_new_region) + add $32,%rsp + pop %rax + ret +#else + pop %rdx + mov %rdi,%rax + mov %rax,%rsi # size, a second parm to the __bound_new_region + + add $15 + 1,%rax # add one extra to separate regions + and $-16,%rax + jz p3 + + + sub %rax,%rsp + mov %rsp,%rdi # pointer, a first parm to the __bound_new_region + mov %rsp,%rax + + push %rdx + push %rax + call _(__bound_new_region) + pop %rax + pop %rdx + +p3: + push %rdx + ret +#endif + +/* ---------------------------------------------- */ +#endif diff --git a/tinycc/lib/alloca.S b/tinycc/lib/alloca.S new file mode 100644 index 0000000..6ebafd7 --- /dev/null +++ b/tinycc/lib/alloca.S @@ -0,0 +1,85 @@ +/* ---------------------------------------------- */ +/* alloca.S */ + +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif + +/* ---------------------------------------------- */ +#if defined __i386__ + +.globl _(alloca), _(__alloca) +_(alloca): +_(__alloca): + push %ebp + mov %esp,%ebp + mov 8(%ebp),%eax + add $3,%eax + and $-4,%eax +#ifdef _WIN32 + jmp .+16 #p2 +p1: + sub $4096,%esp + sub $4096,%eax + test %eax,(%esp) +p2: + cmp $4096,%eax + jae p1 +#endif + sub %eax,%esp + mov 4(%ebp),%eax + mov 0(%ebp),%ebp + add $8,%esp + push %eax + lea 8(%esp),%eax + ret + +/* ---------------------------------------------- */ +#elif defined __x86_64__ + +.globl _(alloca) +_(alloca): + pop %rdx +#ifdef _WIN32 + mov %rcx,%rax +#else + mov %rdi,%rax +#endif + add $15,%rax + and $-16,%rax + jz p3 + +#ifdef _WIN32 +p1: + cmp $4096,%rax + jbe p2 + test %rax,-4096(%rsp) + sub $4096,%rsp + sub $4096,%rax + jmp p1 +p2: +#endif + sub %rax,%rsp + mov %rsp,%rax +p3: + push %rdx + ret + +/* ---------------------------------------------- */ +#elif defined __arm__ + + .text + .align 2 + .global alloca + .type alloca, %function +alloca: + rsb sp, r0, sp + bic sp, sp, #7 + mov r0, sp + mov pc, lr + .size alloca, .-alloca + +/* ---------------------------------------------- */ +#endif diff --git a/tinycc/lib/armeabi.c b/tinycc/lib/armeabi.c new file mode 100644 index 0000000..6ade65e --- /dev/null +++ b/tinycc/lib/armeabi.c @@ -0,0 +1,544 @@ +/* TCC ARM runtime EABI + Copyright (C) 2013 Thomas Preud'homme + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.*/ + +#ifdef __TINYC__ +#define INT_MIN (-2147483647 - 1) +#define INT_MAX 2147483647 +#define UINT_MAX 0xffffffff +#define LONG_MIN (-2147483647L - 1) +#define LONG_MAX 2147483647L +#define ULONG_MAX 0xffffffffUL +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-9223372036854775807LL - 1) +#define ULLONG_MAX 0xffffffffffffffffULL +#else +#include <limits.h> +#endif + +/* We rely on the little endianness and EABI calling convention for this to + work */ + +typedef struct double_unsigned_struct { + unsigned low; + unsigned high; +} double_unsigned_struct; + +typedef struct unsigned_int_struct { + unsigned low; + int high; +} unsigned_int_struct; + +#define REGS_RETURN(name, type) \ + void name ## _return(type ret) {} + + +/* Float helper functions */ + +#define FLOAT_EXP_BITS 8 +#define FLOAT_FRAC_BITS 23 + +#define DOUBLE_EXP_BITS 11 +#define DOUBLE_FRAC_BITS 52 + +#define ONE_EXP(type) ((1 << (type ## _EXP_BITS - 1)) - 1) + +REGS_RETURN(unsigned_int_struct, unsigned_int_struct) +REGS_RETURN(double_unsigned_struct, double_unsigned_struct) + +/* float -> integer: (sign) 1.fraction x 2^(exponent - exp_for_one) */ + + +/* float to [unsigned] long long conversion */ +#define DEFINE__AEABI_F2XLZ(name, with_sign) \ +void __aeabi_ ## name(unsigned val) \ +{ \ + int exp, high_shift, sign; \ + double_unsigned_struct ret; \ + \ + /* compute sign */ \ + sign = val >> 31; \ + \ + /* compute real exponent */ \ + exp = val >> FLOAT_FRAC_BITS; \ + exp &= (1 << FLOAT_EXP_BITS) - 1; \ + exp -= ONE_EXP(FLOAT); \ + \ + /* undefined behavior if truncated value cannot be represented */ \ + if (with_sign) { \ + if (exp > 62) /* |val| too big, double cannot represent LLONG_MAX */ \ + return; \ + } else { \ + if ((sign && exp >= 0) || exp > 63) /* if val < 0 || val too big */ \ + return; \ + } \ + \ + val &= (1 << FLOAT_FRAC_BITS) - 1; \ + if (exp >= 32) { \ + ret.high = 1 << (exp - 32); \ + if (exp - 32 >= FLOAT_FRAC_BITS) { \ + ret.high |= val << (exp - 32 - FLOAT_FRAC_BITS); \ + ret.low = 0; \ + } else { \ + high_shift = FLOAT_FRAC_BITS - (exp - 32); \ + ret.high |= val >> high_shift; \ + ret.low = val << (32 - high_shift); \ + } \ + } else { \ + ret.high = 0; \ + ret.low = 1 << exp; \ + if (exp > FLOAT_FRAC_BITS) \ + ret.low |= val << (exp - FLOAT_FRAC_BITS); \ + else \ + ret.low |= val >> (FLOAT_FRAC_BITS - exp); \ + } \ + \ + /* encode negative integer using 2's complement */ \ + if (with_sign && sign) { \ + ret.low = ~ret.low; \ + ret.high = ~ret.high; \ + if (ret.low == UINT_MAX) { \ + ret.low = 0; \ + ret.high++; \ + } else \ + ret.low++; \ + } \ + \ + double_unsigned_struct_return(ret); \ +} + +/* float to unsigned long long conversion */ +DEFINE__AEABI_F2XLZ(f2ulz, 0) + +/* float to long long conversion */ +DEFINE__AEABI_F2XLZ(f2lz, 1) + +/* double to [unsigned] long long conversion */ +#define DEFINE__AEABI_D2XLZ(name, with_sign) \ +void __aeabi_ ## name(double_unsigned_struct val) \ +{ \ + int exp, high_shift, sign; \ + double_unsigned_struct ret; \ + \ + if ((val.high & ~0x80000000) == 0 && val.low == 0) { \ + ret.low = ret.high = 0; \ + goto _ret_; \ + } \ + \ + /* compute sign */ \ + sign = val.high >> 31; \ + \ + /* compute real exponent */ \ + exp = (val.high >> (DOUBLE_FRAC_BITS - 32)); \ + exp &= (1 << DOUBLE_EXP_BITS) - 1; \ + exp -= ONE_EXP(DOUBLE); \ + \ + /* undefined behavior if truncated value cannot be represented */ \ + if (with_sign) { \ + if (exp > 62) /* |val| too big, double cannot represent LLONG_MAX */ \ + return; \ + } else { \ + if ((sign && exp >= 0) || exp > 63) /* if val < 0 || val too big */ \ + return; \ + } \ + \ + val.high &= (1 << (DOUBLE_FRAC_BITS - 32)) - 1; \ + if (exp >= 32) { \ + ret.high = 1 << (exp - 32); \ + if (exp >= DOUBLE_FRAC_BITS) { \ + high_shift = exp - DOUBLE_FRAC_BITS; \ + ret.high |= val.high << high_shift; \ + ret.high |= val.low >> (32 - high_shift); \ + ret.low = val.low << high_shift; \ + } else { \ + high_shift = DOUBLE_FRAC_BITS - exp; \ + ret.high |= val.high >> high_shift; \ + ret.low = val.high << (32 - high_shift); \ + ret.low |= val.low >> high_shift; \ + } \ + } else { \ + ret.high = 0; \ + ret.low = 1 << exp; \ + if (exp > DOUBLE_FRAC_BITS - 32) { \ + high_shift = exp - DOUBLE_FRAC_BITS - 32; \ + ret.low |= val.high << high_shift; \ + ret.low |= val.low >> (32 - high_shift); \ + } else \ + ret.low |= val.high >> (DOUBLE_FRAC_BITS - 32 - exp); \ + } \ + \ + /* encode negative integer using 2's complement */ \ + if (with_sign && sign) { \ + ret.low = ~ret.low; \ + ret.high = ~ret.high; \ + if (ret.low == UINT_MAX) { \ + ret.low = 0; \ + ret.high++; \ + } else \ + ret.low++; \ + } \ + \ +_ret_: \ + double_unsigned_struct_return(ret); \ +} + +/* double to unsigned long long conversion */ +DEFINE__AEABI_D2XLZ(d2ulz, 0) + +/* double to long long conversion */ +DEFINE__AEABI_D2XLZ(d2lz, 1) + +/* long long to float conversion */ +#define DEFINE__AEABI_XL2F(name, with_sign) \ +unsigned __aeabi_ ## name(unsigned long long v) \ +{ \ + int s /* shift */, flb /* first lost bit */, sign = 0; \ + unsigned p = 0 /* power */, ret; \ + double_unsigned_struct val; \ + \ + /* fraction in negative float is encoded in 1's complement */ \ + if (with_sign && (v & (1ULL << 63))) { \ + sign = 1; \ + v = ~v + 1; \ + } \ + val.low = v; \ + val.high = v >> 32; \ + /* fill fraction bits */ \ + for (s = 31, p = 1 << 31; p && !(val.high & p); s--, p >>= 1); \ + if (p) { \ + ret = val.high & (p - 1); \ + if (s < FLOAT_FRAC_BITS) { \ + ret <<= FLOAT_FRAC_BITS - s; \ + ret |= val.low >> (32 - (FLOAT_FRAC_BITS - s)); \ + flb = (val.low >> (32 - (FLOAT_FRAC_BITS - s - 1))) & 1; \ + } else { \ + flb = (ret >> (s - FLOAT_FRAC_BITS - 1)) & 1; \ + ret >>= s - FLOAT_FRAC_BITS; \ + } \ + s += 32; \ + } else { \ + for (s = 31, p = 1 << 31; p && !(val.low & p); s--, p >>= 1); \ + if (p) { \ + ret = val.low & (p - 1); \ + if (s <= FLOAT_FRAC_BITS) { \ + ret <<= FLOAT_FRAC_BITS - s; \ + flb = 0; \ + } else { \ + flb = (ret >> (s - FLOAT_FRAC_BITS - 1)) & 1; \ + ret >>= s - FLOAT_FRAC_BITS; \ + } \ + } else \ + return 0; \ + } \ + if (flb) \ + ret++; \ + \ + /* fill exponent bits */ \ + ret |= (s + ONE_EXP(FLOAT)) << FLOAT_FRAC_BITS; \ + \ + /* fill sign bit */ \ + ret |= sign << 31; \ + \ + return ret; \ +} + +/* unsigned long long to float conversion */ +DEFINE__AEABI_XL2F(ul2f, 0) + +/* long long to float conversion */ +DEFINE__AEABI_XL2F(l2f, 1) + +/* long long to double conversion */ +#define __AEABI_XL2D(name, with_sign) \ +void __aeabi_ ## name(unsigned long long v) \ +{ \ + int s /* shift */, high_shift, sign = 0; \ + unsigned tmp, p = 0; \ + double_unsigned_struct val, ret; \ + \ + /* fraction in negative float is encoded in 1's complement */ \ + if (with_sign && (v & (1ULL << 63))) { \ + sign = 1; \ + v = ~v + 1; \ + } \ + val.low = v; \ + val.high = v >> 32; \ + \ + /* fill fraction bits */ \ + for (s = 31, p = 1 << 31; p && !(val.high & p); s--, p >>= 1); \ + if (p) { \ + tmp = val.high & (p - 1); \ + if (s < DOUBLE_FRAC_BITS - 32) { \ + high_shift = DOUBLE_FRAC_BITS - 32 - s; \ + ret.high = tmp << high_shift; \ + ret.high |= val.low >> (32 - high_shift); \ + ret.low = val.low << high_shift; \ + } else { \ + high_shift = s - (DOUBLE_FRAC_BITS - 32); \ + ret.high = tmp >> high_shift; \ + ret.low = tmp << (32 - high_shift); \ + ret.low |= val.low >> high_shift; \ + if ((val.low >> (high_shift - 1)) & 1) { \ + if (ret.low == UINT_MAX) { \ + ret.high++; \ + ret.low = 0; \ + } else \ + ret.low++; \ + } \ + } \ + s += 32; \ + } else { \ + for (s = 31, p = 1 << 31; p && !(val.low & p); s--, p >>= 1); \ + if (p) { \ + tmp = val.low & (p - 1); \ + if (s <= DOUBLE_FRAC_BITS - 32) { \ + high_shift = DOUBLE_FRAC_BITS - 32 - s; \ + ret.high = tmp << high_shift; \ + ret.low = 0; \ + } else { \ + high_shift = s - (DOUBLE_FRAC_BITS - 32); \ + ret.high = tmp >> high_shift; \ + ret.low = tmp << (32 - high_shift); \ + } \ + } else { \ + ret.high = ret.low = 0; \ + goto _ret_; \ + } \ + } \ + \ + /* fill exponent bits */ \ + ret.high |= (s + ONE_EXP(DOUBLE)) << (DOUBLE_FRAC_BITS - 32); \ + \ + /* fill sign bit */ \ + ret.high |= sign << 31; \ + \ +_ret_: \ + double_unsigned_struct_return(ret); \ +} + +/* unsigned long long to double conversion */ +__AEABI_XL2D(ul2d, 0) + +/* long long to double conversion */ +__AEABI_XL2D(l2d, 1) + + +/* Long long helper functions */ + +/* TODO: add error in case of den == 0 (see §4.3.1 and §4.3.2) */ + +#define define_aeabi_xdivmod_signed_type(basetype, type) \ +typedef struct type { \ + basetype quot; \ + unsigned basetype rem; \ +} type + +#define define_aeabi_xdivmod_unsigned_type(basetype, type) \ +typedef struct type { \ + basetype quot; \ + basetype rem; \ +} type + +#define AEABI_UXDIVMOD(name,type, rettype, typemacro) \ +static inline rettype aeabi_ ## name (type num, type den) \ +{ \ + rettype ret; \ + type quot = 0; \ + \ + /* Increase quotient while it is less than numerator */ \ + while (num >= den) { \ + type q = 1; \ + \ + /* Find closest power of two */ \ + while ((q << 1) * den <= num && q * den <= typemacro ## _MAX / 2) \ + q <<= 1; \ + \ + /* Compute difference between current quotient and numerator */ \ + num -= q * den; \ + quot += q; \ + } \ + ret.quot = quot; \ + ret.rem = num; \ + return ret; \ +} + +#define __AEABI_XDIVMOD(name, type, uiname, rettype, urettype, typemacro) \ +void __aeabi_ ## name(type numerator, type denominator) \ +{ \ + unsigned type num, den; \ + urettype uxdiv_ret; \ + rettype ret; \ + \ + if (numerator >= 0) \ + num = numerator; \ + else \ + num = 0 - numerator; \ + if (denominator >= 0) \ + den = denominator; \ + else \ + den = 0 - denominator; \ + uxdiv_ret = aeabi_ ## uiname(num, den); \ + /* signs differ */ \ + if ((numerator & typemacro ## _MIN) != (denominator & typemacro ## _MIN)) \ + ret.quot = 0 - uxdiv_ret.quot; \ + else \ + ret.quot = uxdiv_ret.quot; \ + if (numerator < 0) \ + ret.rem = 0 - uxdiv_ret.rem; \ + else \ + ret.rem = uxdiv_ret.rem; \ + \ + rettype ## _return(ret); \ +} + +define_aeabi_xdivmod_signed_type(long long, lldiv_t); +define_aeabi_xdivmod_unsigned_type(unsigned long long, ulldiv_t); +define_aeabi_xdivmod_signed_type(int, idiv_t); +define_aeabi_xdivmod_unsigned_type(unsigned, uidiv_t); + +REGS_RETURN(lldiv_t, lldiv_t) +REGS_RETURN(ulldiv_t, ulldiv_t) +REGS_RETURN(idiv_t, idiv_t) +REGS_RETURN(uidiv_t, uidiv_t) + +AEABI_UXDIVMOD(uldivmod, unsigned long long, ulldiv_t, ULLONG) + +__AEABI_XDIVMOD(ldivmod, long long, uldivmod, lldiv_t, ulldiv_t, LLONG) + +void __aeabi_uldivmod(unsigned long long num, unsigned long long den) +{ + ulldiv_t_return(aeabi_uldivmod(num, den)); +} + +void __aeabi_llsl(double_unsigned_struct val, int shift) +{ + double_unsigned_struct ret; + + if (shift >= 32) { + val.high = val.low; + val.low = 0; + shift -= 32; + } + if (shift > 0) { + ret.low = val.low << shift; + ret.high = (val.high << shift) | (val.low >> (32 - shift)); + double_unsigned_struct_return(ret); + return; + } + double_unsigned_struct_return(val); +} + +#define aeabi_lsr(val, shift, fill, type) \ + type ## _struct ret; \ + \ + if (shift >= 32) { \ + val.low = val.high; \ + val.high = fill; \ + shift -= 32; \ + } \ + if (shift > 0) { \ + ret.high = val.high >> shift; \ + ret.low = (val.high << (32 - shift)) | (val.low >> shift); \ + type ## _struct_return(ret); \ + return; \ + } \ + type ## _struct_return(val); + +void __aeabi_llsr(double_unsigned_struct val, int shift) +{ + aeabi_lsr(val, shift, 0, double_unsigned); +} + +void __aeabi_lasr(unsigned_int_struct val, int shift) +{ + aeabi_lsr(val, shift, val.high >> 31, unsigned_int); +} + + +/* Integer division functions */ + +AEABI_UXDIVMOD(uidivmod, unsigned, uidiv_t, UINT) + +int __aeabi_idiv(int numerator, int denominator) +{ + unsigned num, den; + uidiv_t ret; + + if (numerator >= 0) + num = numerator; + else + num = 0 - numerator; + if (denominator >= 0) + den = denominator; + else + den = 0 - denominator; + ret = aeabi_uidivmod(num, den); + if ((numerator & INT_MIN) != (denominator & INT_MIN)) /* signs differ */ + ret.quot *= -1; + return ret.quot; +} + +unsigned __aeabi_uidiv(unsigned num, unsigned den) +{ + return aeabi_uidivmod(num, den).quot; +} + +__AEABI_XDIVMOD(idivmod, int, uidivmod, idiv_t, uidiv_t, INT) + +void __aeabi_uidivmod(unsigned num, unsigned den) +{ + uidiv_t_return(aeabi_uidivmod(num, den)); +} + +/* Some targets do not have all eabi calls (OpenBSD) */ +typedef __SIZE_TYPE__ size_t; +extern void *memcpy(void *dest, const void *src, size_t n); +extern void *memmove(void *dest, const void *src, size_t n); +extern void *memset(void *s, int c, size_t n); + +void * +__aeabi_memcpy (void *dest, const void *src, size_t n) +{ + return memcpy (dest, src, n); +} + +void * +__aeabi_memmove (void *dest, const void *src, size_t n) +{ + return memmove (dest, src, n); +} + +void * +__aeabi_memmove4 (void *dest, const void *src, size_t n) +{ + return memmove (dest, src, n); +} + +void * +__aeabi_memmove8 (void *dest, const void *src, size_t n) +{ + return memmove (dest, src, n); +} + +void * +__aeabi_memset (void *s, size_t n, int c) +{ + return memset (s, c, n); +} diff --git a/tinycc/lib/armflush.c b/tinycc/lib/armflush.c new file mode 100644 index 0000000..c379e43 --- /dev/null +++ b/tinycc/lib/armflush.c @@ -0,0 +1,51 @@ +/* armflush.c - flush the instruction cache + + __clear_cache is used in tccrun.c, It is a built-in + intrinsic with gcc. However tcc in order to compile + itself needs this function */ + +#ifdef __TINYC__ + +/* syscall wrapper */ +unsigned _tccsyscall(unsigned syscall_nr, ...); + +/* arm-tcc supports only fake asm currently */ +__asm__( + ".global _tccsyscall\n" + "_tccsyscall:\n" + "push {r7, lr}\n\t" + "mov r7, r0\n\t" + "mov r0, r1\n\t" + "mov r1, r2\n\t" + "mov r2, r3\n\t" + "svc #0\n\t" + "pop {r7, pc}" + ); + +/* from unistd.h: */ +#if defined(__thumb__) || defined(__ARM_EABI__) +# define __NR_SYSCALL_BASE 0x0 +#else +# define __NR_SYSCALL_BASE 0x900000 +#endif +#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000) +#define __ARM_NR_cacheflush (__ARM_NR_BASE+2) + +#define syscall _tccsyscall + +#else + +#define _GNU_SOURCE +#include <unistd.h> +#include <sys/syscall.h> +#include <stdio.h> + +#endif + +/* Flushing for tccrun */ +void __clear_cache(void *beginning, void *end) +{ +/* __ARM_NR_cacheflush is kernel private and should not be used in user space. + * However, there is no ARM asm parser in tcc so we use it for now */ + syscall(__ARM_NR_cacheflush, beginning, end, 0); +} diff --git a/tinycc/lib/atomic.S b/tinycc/lib/atomic.S new file mode 100644 index 0000000..29fdbee --- /dev/null +++ b/tinycc/lib/atomic.S @@ -0,0 +1,858 @@ +/* ---------------------------------------------- */ +/* This file implements for arm/arm64/riscv: + * __atomic_compare_exchange_1 + * __atomic_compare_exchange_2 + * __atomic_compare_exchange_4 + * __atomic_compare_exchange_8 + */ + +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif + +#if defined __i386__ + .text + .align 2 + + .global _(__atomic_test_and_set) + .type _(__atomic_test_and_set), %function +_(__atomic_test_and_set): + movl 4(%esp), %edx + movl $1, %eax + xchgb (%edx), %al + ret + .size _(__atomic_test_and_set), .-_(__atomic_test_and_set) + + .global _(__atomic_clear) + .type _(__atomic_clear), %function +_(__atomic_clear): + movl 4(%esp), %edx + xorl %eax, %eax + xchgb (%edx), %al + ret + .size _(__atomic_clear), .-_(__atomic_clear) + +#elif defined __x86_64__ + .text + .align 2 + + .global _(__atomic_test_and_set) + .type _(__atomic_test_and_set), %function +_(__atomic_test_and_set): + movl $1, %eax + xchgb (%rdi), %al + ret + .size _(__atomic_test_and_set), .-_(__atomic_test_and_set) + + .global _(__atomic_clear) + .type _(__atomic_clear), %function +_(__atomic_clear): + xorl %eax, %eax + xchgb (%rdi), %al + ret + .size _(__atomic_clear), .-_(__atomic_clear) + +#elif defined __arm__ + +#ifndef __TINYC__ + .arch armv6k + .syntax unified +#endif + .text + .align 2 + + .global _(fetch_and_add_arm) + .type _(fetch_and_add_arm), %function +_(fetch_and_add_arm): + mcr p15, #0, r0, c7, c10, #5 +.L0: + ldrex r3, [r0] + add r3, r3, r1 + strex r2, r3, [r0] + cmp r2, #0 + bne .L0 + mcr p15, #0, r0, c7, c10, #5 + bx lr + .size _(fetch_and_add_arm), .-_(fetch_and_add_arm) + + .global _(__atomic_test_and_set) + .type _(__atomic_test_and_set), %function +_(__atomic_test_and_set): +#ifdef __TINYC__ + .int 0xe92d4030 + .int 0xee070fba + .int 0xe5d03000 + .int 0xe24dd014 + .int 0xe1a05000 + .int 0xe2533000 + .int 0xe1a04001 + .int 0x13a03001 + .int 0xee070fba + .int 0xe5cd300f + .int 0xe3a03001 + .int 0xe1a02003 + .int 0xe28d100f + .int 0xe1a00005 + .int 0xe58d4004 + .int 0xe58d4000 + .int 0xeb000009 + .int 0xe3500000 + .int 0x0afffff6 + .int 0xe5dd000f + .int 0xe28dd014 + .int 0xe8bd8030 +#else + push {r4, r5, lr} + mcr p15, 0, r0, c7, c10, 5 + ldrb r3, [r0] + sub sp, sp, #20 + mov r5, r0 + subs r3, r3, #0 + mov r4, r1 + movne r3, #1 + mcr p15, 0, r0, c7, c10, 5 + strb r3, [sp, #15] +.L20: + mov r3, #1 + mov r2, r3 + add r1, sp, #15 + mov r0, r5 + str r4, [sp, #4] + str r4, [sp] + bl __atomic_compare_exchange_1 + cmp r0, #0 + beq .L20 + ldrb r0, [sp, #15] + add sp, sp, #20 + pop {r4, r5, pc} +#endif + .size _(__atomic_test_and_set), .-_(__atomic_test_and_set) + + .global _(__atomic_clear) + .type _(__atomic_clear), %function +_(__atomic_clear): +#ifdef __TINYC__ + .int 0xe3a03000 + .int 0xee070fba + .int 0xe5c03000 + .int 0xee070fba + .int 0xe12fff1e +#else + mov r3, #0 + mcr p15, 0, r0, c7, c10, 5 + strb r3, [r0] + mcr p15, 0, r0, c7, c10, 5 + bx lr +#endif + .size _(__atomic_clear), .-_(__atomic_clear) + + .global _(__atomic_compare_exchange_1) + .type _(__atomic_compare_exchange_1), %function +_(__atomic_compare_exchange_1): +#ifdef __TINYC__ + .int 0xe52de004 + .int 0xe5d13000 + .int 0xf57ff05b + .int 0xe1d0cf9f + .int 0xe15c0003 + .int 0x1a000002 + .int 0xe1c0ef92 + .int 0xe35e0000 + .int 0x1afffff9 + .int 0x03a00001 + .int 0x13a00000 + .int 0xf57ff05b + .int 0x15c1c000 + .int 0xe49df004 +#else + str lr, [sp, #-4]! + ldrb r3, [r1] + mcr p15, 0, r0, c7, c10, 5 +.L1: + ldrexb ip, [r0] + cmp ip, r3 + bne .L2 + strexb lr, r2, [r0] + cmp lr, #0 + bne .L1 +.L2: + mcr p15, 0, r0, c7, c10, 5 + moveq r0, #1 + movne r0, #0 + strbne ip, [r1] + ldr pc, [sp], #4 +#endif + .size _(__atomic_compare_exchange_1), .-_(__atomic_compare_exchange_1) + + .global _(__atomic_compare_exchange_2) + .type _(__atomic_compare_exchange_2), %function +_(__atomic_compare_exchange_2): +#ifdef __TINYC__ + .int 0xe52de004 + .int 0xe1d130b0 + .int 0xf57ff05b + .int 0xe1f0cf9f + .int 0xe15c0003 + .int 0x1a000002 + .int 0xe1e0ef92 + .int 0xe35e0000 + .int 0x1afffff9 + .int 0x03a00001 + .int 0x13a00000 + .int 0xf57ff05b + .int 0x11c1c0b0 + .int 0xe49df004 +#else + str lr, [sp, #-4]! + ldrh r3, [r1] + mcr p15, 0, r0, c7, c10, 5 +.L3: + ldrexh ip, [r0] + cmp ip, r3 + bne .L4 + strexh lr, r2, [r0] + cmp lr, #0 + bne .L3 +.L4: + mcr p15, 0, r0, c7, c10, 5 + moveq r0, #1 + movne r0, #0 + strhne ip, [r1] + ldr pc, [sp], #4 +#endif + .size _(__atomic_compare_exchange_2), .-_(__atomic_compare_exchange_2) + + .global _(__atomic_compare_exchange_4) + .type _(__atomic_compare_exchange_4), %function +_(__atomic_compare_exchange_4): +#ifdef __TINYC__ + .int 0xe52de004 + .int 0xe5913000 + .int 0xf57ff05b + .int 0xe190cf9f + .int 0xe15c0003 + .int 0x1a000002 + .int 0xe180ef92 + .int 0xe35e0000 + .int 0x1afffff9 + .int 0x03a00001 + .int 0x13a00000 + .int 0xf57ff05b + .int 0x1581c000 + .int 0xe49df004 +#else + str lr, [sp, #-4]! + ldr r3, [r1] + mcr p15, 0, r0, c7, c10, 5 +.L5: + ldrex ip, [r0] + cmp ip, r3 + bne .L6 + strex lr, r2, [r0] + cmp lr, #0 + bne .L5 +.L6: + mcr p15, 0, r0, c7, c10, 5 + moveq r0, #1 + movne r0, #0 + strne ip, [r1] + ldr pc, [sp], #4 +#endif + .size _(__atomic_compare_exchange_4), .-_(__atomic_compare_exchange_4) + +/* ---------------------------------------------- */ +#elif defined __aarch64__ + + .text + .align 2 + + .global _(fetch_and_add_arm64) + .type _(fetch_and_add_arm64), %function +_(fetch_and_add_arm64): +#ifdef __TINYC__ + .int 0x885f7c02 + .int 0x0b010042 + .int 0x8803fc02 + .int 0x35ffffa3 + .int 0xd5033bbf + .int 0xd65f03c0 +#else + ldxr w2, [x0] + add w2, w2, w1 + stlxr w3, w2, [x0] + cbnz w3, _(fetch_and_add_arm64) + dmb ish + ret +#endif + .size _(fetch_and_add_arm64), .-_(fetch_and_add_arm64) + + .global _(__atomic_test_and_set) + .type _(__atomic_test_and_set), %function +_(__atomic_test_and_set): +#ifdef __TINYC__ + .int 0xa9bf7bfd + .int 0xaa0003e1 + .int 0x52800020 + .int 0x910003fd + .int 0x2a0003f0 + .int 0x085ffc20 + .int 0x0811fc30 + .int 0x35ffffd1 + .int 0xa8c17bfd + .int 0xd65f03c0 +#else + stp x29, x30, [sp, -16]! + mov x1, x0 + mov w0, 1 + mov x29, sp + mov w16, w0 +.L20: + ldaxrb w0, [x1] + stlxrb w17, w16, [x1] + cbnz w17, .L20 + ldp x29, x30, [sp], 16 + ret +#endif + .size _(__atomic_test_and_set), .-_(__atomic_test_and_set) + + .global _(__atomic_clear) + .type _(__atomic_clear), %function +_(__atomic_clear): +#ifdef __TINYC__ + .int 0x089ffc1f + .int 0xd65f03c0 +#else + stlrb wzr, [x0] + ret +#endif + .size _(__atomic_clear), .-_(__atomic_clear) + + .global _(__atomic_compare_exchange_1) + .type _(__atomic_compare_exchange_1), %function +_(__atomic_compare_exchange_1): +#ifdef __TINYC__ + .int 0xa9be7bfd + .int 0x910003fd + .int 0xa90153f3 + .int 0xaa0103f3 + .int 0x12001c41 + .int 0xaa0003e2 + .int 0x39400274 + .int 0x2a1403e0 + .int 0x53001c10 + .int 0x085ffc40 + .int 0x6b10001f + .int 0x54000061 + .int 0x0811fc41 + .int 0x35ffff91 + .int 0x6b34001f + .int 0x1a9f17e1 + .int 0x54000040 + .int 0x39000260 + .int 0x2a0103e0 + .int 0xa94153f3 + .int 0xa8c27bfd + .int 0xd65f03c0 +#else + stp x29, x30, [sp, -32]! + mov x29, sp + stp x19, x20, [sp, 16] + mov x19, x1 + and w1, w2, 255 + mov x2, x0 + ldrb w20, [x19] + mov w0, w20 + uxtb w16, w0 +.L1: + ldaxrb w0, [x2] + cmp w0, w16 + b.ne .L2 + stlxrb w17, w1, [x2] + cbnz w17, .L1 +.L2: + cmp w0, w20, uxtb + cset w1, eq + beq .L3 + strb w0, [x19] +.L3: + mov w0, w1 + ldp x19, x20, [sp, 16] + ldp x29, x30, [sp], 32 + ret +#endif + .size _(__atomic_compare_exchange_1), .-_(__atomic_compare_exchange_1) + + .global _(__atomic_compare_exchange_2) + .type _(__atomic_compare_exchange_2), %function +_(__atomic_compare_exchange_2): +#ifdef __TINYC__ + .int 0xa9be7bfd + .int 0x910003fd + .int 0xa90153f3 + .int 0xaa0103f3 + .int 0x12003c41 + .int 0xaa0003e2 + .int 0x79400274 + .int 0x2a1403e0 + .int 0x53003c10 + .int 0x485ffc40 + .int 0x6b10001f + .int 0x54000061 + .int 0x4811fc41 + .int 0x35ffff91 + .int 0x6b34201f + .int 0x1a9f17e1 + .int 0x54000040 + .int 0x79000260 + .int 0x2a0103e0 + .int 0xa94153f3 + .int 0xa8c27bfd + .int 0xd65f03c0 +#else + stp x29, x30, [sp, -32]! + mov x29, sp + stp x19, x20, [sp, 16] + mov x19, x1 + and w1, w2, 65535 + mov x2, x0 + ldrh w20, [x19] + mov w0, w20 + uxth w16, w0 +.L4: + ldaxrh w0, [x2] + cmp w0, w16 + b.ne .L5 + stlxrh w17, w1, [x2] + cbnz w17, .L4 +.L5: + cmp w0, w20, uxth + cset w1, eq + beq .L6 + strh w0, [x19] +.L6: + mov w0, w1 + ldp x19, x20, [sp, 16] + ldp x29, x30, [sp], 32 + ret +#endif + .size _(__atomic_compare_exchange_2), .-_(__atomic_compare_exchange_2) + + .global _(__atomic_compare_exchange_4) + .type _(__atomic_compare_exchange_4), %function +_(__atomic_compare_exchange_4): +#ifdef __TINYC__ + .int 0xa9be7bfd + .int 0x910003fd + .int 0xa90153f3 + .int 0xaa0103f3 + .int 0x2a0203e1 + .int 0xaa0003e2 + .int 0xb9400274 + .int 0x2a1403e0 + .int 0x2a0003f0 + .int 0x885ffc40 + .int 0x6b10001f + .int 0x54000061 + .int 0x8811fc41 + .int 0x35ffff91 + .int 0x6b14001f + .int 0x1a9f17e1 + .int 0x54000040 + .int 0xb9000260 + .int 0x2a0103e0 + .int 0xa94153f3 + .int 0xa8c27bfd + .int 0xd65f03c0 +#else + stp x29, x30, [sp, -32]! + mov x29, sp + stp x19, x20, [sp, 16] + mov x19, x1 + mov w1, w2 + mov x2, x0 + ldr w20, [x19] + mov w0, w20 + mov w16, w0 +.L7: + ldaxr w0, [x2] + cmp w0, w16 + b.ne .L8 + stlxr w17, w1, [x2] + cbnz w17, .L7 +.L8: + cmp w0, w20 + cset w1, eq + beq .L9 + str w0, [x19] +.L9: + mov w0, w1 + ldp x19, x20, [sp, 16] + ldp x29, x30, [sp], 32 + ret +#endif + .size _(__atomic_compare_exchange_4), .-_(__atomic_compare_exchange_4) + + .global _(__atomic_compare_exchange_8) + .type _(__atomic_compare_exchange_8), %function +_(__atomic_compare_exchange_8): +#ifdef __TINYC__ + .int 0xa9be7bfd + .int 0x910003fd + .int 0xa90153f3 + .int 0xaa0103f3 + .int 0xaa0203e1 + .int 0xaa0003e2 + .int 0xf9400274 + .int 0xaa1403e0 + .int 0xaa0003f0 + .int 0xc85ffc40 + .int 0xeb10001f + .int 0x54000061 + .int 0xc811fc41 + .int 0x35ffff91 + .int 0xeb14001f + .int 0x1a9f17e1 + .int 0x54000040 + .int 0xf9000260 + .int 0x2a0103e0 + .int 0xa94153f3 + .int 0xa8c27bfd + .int 0xd65f03c0 +#else + stp x29, x30, [sp, -32]! + mov x29, sp + stp x19, x20, [sp, 16] + mov x19, x1 + mov x1, x2 + mov x2, x0 + ldr x20, [x19] + mov x0, x20 + mov x16, x0 +.L10: + ldaxr x0, [x2] + cmp x0, x16 + b.ne .L11 + stlxr w17, x1, [x2] + cbnz w17, .L10 +.L11: + cmp x0, x20 + cset w1, eq + beq .L12 + str x0, [x19] +.L12: + mov w0, w1 + ldp x19, x20, [sp, 16] + ldp x29, x30, [sp], 32 + ret +#endif + .size _(__atomic_compare_exchange_8), .-_(__atomic_compare_exchange_8) + +/* ---------------------------------------------- */ +#elif defined __riscv + + .text + .align 2 + + .global _(fetch_and_add_riscv64) + .type _(fetch_and_add_riscv64), %function +_(fetch_and_add_riscv64): +#ifdef __TINYC__ + .int 0x0f50000f + .int 0x004b5202f + .short 0x8082 +#else + fence iorw,ow + amoadd.w.aq zero,a1,0(a0) + ret +#endif + .size _(fetch_and_add_riscv64), .-_(fetch_and_add_riscv64) + + .global _(__atomic_test_and_set) + .type _(__atomic_test_and_set), %function +_(__atomic_test_and_set): +#ifdef __TINYC__ + .int 0x00357793 + .int 0x0037979b + .short 0x4685 + .short 0x9971 + .int 0x00f696bb + .int 0x0f50000f + .int 0x44d5272f + .int 0x00f7553b + .int 0x0ff57513 + .short 0x8082 +#else + andi a5,a0,3 + slliw a5,a5,3 + li a3,1 + andi a0,a0,-4 + sllw a3,a3,a5 + fence iorw,ow; amoor.w.aq a4,a3,0(a0) + srlw a0,a4,a5 + andi a0,a0,0xff + ret +#endif + .size _(__atomic_test_and_set), .-_(__atomic_test_and_set) + + .global _(__atomic_clear) + .type _(__atomic_clear), %function +_(__atomic_clear): +#ifdef __TINYC__ + .int 0x0ff0000f + .int 0x00050023 + .int 0x0ff0000f + .short 0x8082 +#else + fence iorw,iorw + sb zero,0(a0) + fence iorw,iorw + ret +#endif + .size _(__atomic_clear), .-_(__atomic_clear) + + .global _(__atomic_compare_exchange_1) + .type _(__atomic_compare_exchange_1), %function +_(__atomic_compare_exchange_1): +#ifdef __TINYC__ + .short 0x1141 + .short 0x86ba + .short 0x873e + .short 0xe406 + .int 0x0ff0000f + .int 0x0005c803 + .int 0xff857893 + .int 0x0008b783 + .short 0x891d + .short 0x050e + .int 0x0ff00693 + .int 0x00a696b3 + .int 0x00a81833 + .int 0x00a61633 + .int 0xfff6c713 + .short 0x8f7d + .int 0x00f6f333 + .short 0x8f51 + .int 0x03031263 + .int 0x1008b32f + .int 0x00f31663 + .int 0x18e8be2f + .int 0xfe0e1ae3 + .int 0x40f30733 + .short 0x879a + .short 0xff69 + .int 0x0ff0000f + .short 0x4505 + .short 0xa801 + .int 0x00a7d7b3 + .int 0x00f58023 + .int 0x0ff0000f + .short 0x4501 + .short 0x60a2 + .short 0x0141 + .short 0x8082 +#else + addi sp,sp,-16 + mv a3,a4 + mv a4,a5 + sd ra,8(sp) + fence + lbu a6,0(a1) + andi a7,a0,-8 + ld a5,0(a7) + andi a0,a0,7 + slli a0,a0,0x3 + li a3,255 + sll a3,a3,a0 + sll a6,a6,a0 + sll a2,a2,a0 +.L1: + not a4,a3 + and a4,a4,a5 + and t1,a3,a5 + or a4,a4,a2 + bne t1,a6,.L4 +.L2: + lr.d t1,(a7) + bne t1,a5,.L3 + sc.d t3,a4,(a7) + bnez t3,.L2 +.L3: + sub a4,t1,a5 + mv a5,t1 + bnez a4,.L1 + fence + li a0,1 + j .L5 +.L4: + srl a5,a5,a0 + sb a5,0(a1) + fence + li a0,0 +.L5: + ld ra,8(sp) + addi sp,sp,16 + jr ra +#endif + .size _(__atomic_compare_exchange_1), .-_(__atomic_compare_exchange_1) + + .global _(__atomic_compare_exchange_2) + .type _(__atomic_compare_exchange_2), %function +_(__atomic_compare_exchange_2): +#ifdef __TINYC__ + .short 0x1141 + .short 0x86ba + .short 0x873e + .short 0xe406 + .int 0x0ff0000f + .int 0x0005d803 + .int 0xff857893 + .short 0x67c1 + .short 0x891d + .int 0x0008b703 + .short 0x050e + .short 0x17fd + .int 0x00a797b3 + .int 0x00a81833 + .int 0x00a61633 + .int 0xfff7c693 + .short 0x8ef9 + .int 0x00e7f333 + .short 0x8ed1 + .int 0x03031263 + .int 0x1008b32f + .int 0x00e31663 + .int 0x18d8be2f + .int 0xfe0e1ae3 + .int 0x40e306b3 + .short 0x871a + .short 0xfee9 + .int 0x0ff0000f + .short 0x4505 + .short 0xa801 + .int 0x00a75733 + .int 0x00e59023 + .int 0x0ff0000f + .short 0x4501 + .short 0x60a2 + .short 0x0141 + .short 0x8082 +#else + addi sp,sp,-16 + mv a3,a4 + mv a4,a5 + sd ra,8(sp) + fence + lhu a6,0(a1) + andi a7,a0,-8 + lui a5,0x10 + andi a0,a0,7 + ld a4,0(a7) + slli a0,a0,0x3 + addi a5,a5,-1 + sll a5,a5,a0 + sll a6,a6,a0 + sll a2,a2,a0 +.L6: + not a3,a5 + and a3,a3,a4 + and t1,a5,a4 + or a3,a3,a2 + bne t1,a6,.L9 +.L7: + lr.d t1,(a7) + bne t1,a4,.L8 + sc.d t3,a3,(a7) + bnez t3,.L7 +.L8: + sub a3,t1,a4 + mv a4,t1 + bnez a3,.L6 + fence + li a0,1 + j .L10 +.L9: + srl a4,a4,a0 + sh a4,0(a1) + fence + li a0,0 +.L10: + ld ra,8(sp) + addi sp,sp,16 + jr ra +#endif + .size _(__atomic_compare_exchange_2), .-_(__atomic_compare_exchange_2) + + .global _(__atomic_compare_exchange_4) + .type _(__atomic_compare_exchange_4), %function +_(__atomic_compare_exchange_4): +#ifdef __TINYC__ + .short 0x419c + .int 0x0f50000f + .int 0x1405272f + .int 0x00f71663 + .int 0x1cc5282f + .int 0xfe081ae3 + .int 0x40f707bb + .int 0x0017b513 + .short 0xc391 + .short 0xc198 + .short 0x8905 + .short 0x8082 +#else + lw a5,0(a1) + fence iorw,ow; +.L11: + lr.w.aq a4,0(a0) + bne a4,a5,.L12 + sc.w.aq a6,a2,0(a0) + bnez a6,.L11 +.L12: + subw a5,a4,a5 + seqz a0,a5 + beq a5,zero,.L13 + sw a4,0(a1) +.L13: + andi a0,a0,1 + ret +#endif + .size _(__atomic_compare_exchange_4), .-_(__atomic_compare_exchange_4) + + .global _(__atomic_compare_exchange_8) + .type _(__atomic_compare_exchange_8), %function +_(__atomic_compare_exchange_8): +#ifdef __TINYC__ + .short 0x619c + .int 0x0f50000f + .int 0x1405372f + .int 0x00f71563 + .int 0x1cc536af + .short 0xfaf5 + .int 0x40f707b3 + .int 0x0017b513 + .short 0xc391 + .short 0xe198 + .short 0x8905 + .short 0x8082 +#else + ld a5,0(a1) + fence iorw,ow; +.L14: + lr.d.aq a4,0(a0) + bne a4,a5,.L15 + sc.d.aq a3,a2,0(a0) + bnez a3,.L14 +.L15: + sub a5,a4,a5 + seqz a0,a5 + beq a5,zero,.L16 + sd a4,0(a1) +.L16: + andi a0,a0,1 + ret +#endif + .size _(__atomic_compare_exchange_8), .-_(__atomic_compare_exchange_8) + +/* ---------------------------------------------- */ +#endif diff --git a/tinycc/lib/bcheck.c b/tinycc/lib/bcheck.c new file mode 100644 index 0000000..b8dd1c5 --- /dev/null +++ b/tinycc/lib/bcheck.c @@ -0,0 +1,2270 @@ +/* + * Tiny C Memory and bounds checker + * + * Copyright (c) 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <setjmp.h> + +#if !defined(__FreeBSD__) \ + && !defined(__FreeBSD_kernel__) \ + && !defined(__DragonFly__) \ + && !defined(__OpenBSD__) \ + && !defined(__APPLE__) \ + && !defined(__NetBSD__) +#include <malloc.h> +#endif + +#if !defined(_WIN32) +#include <unistd.h> +#include <sys/syscall.h> +#endif + +#define BOUND_DEBUG (1) +#define BOUND_STATISTIC (1) + +#if BOUND_DEBUG + #define dprintf(a...) if (print_calls) fprintf(a) +#else + #define dprintf(a...) +#endif + +#ifdef __attribute__ + /* an __attribute__ macro is defined in the system headers */ + #undef __attribute__ +#endif +#define FASTCALL __attribute__((regparm(3))) + +#ifdef _WIN32 +# define DLL_EXPORT __declspec(dllexport) +#else +# define DLL_EXPORT +#endif + +#if defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) \ + || defined(__DragonFly__) \ + || defined(__OpenBSD__) \ + || defined(__NetBSD__) \ + || defined(__dietlibc__) + +#include <sys/mman.h> +#define INIT_SEM() +#define EXIT_SEM() +#define WAIT_SEM() +#define POST_SEM() +#define TRY_SEM() +#define HAVE_MEMALIGN (0) +#define MALLOC_REDIR (0) +#define HAVE_PTHREAD_CREATE (0) +#define HAVE_CTYPE (0) +#define HAVE_ERRNO (0) +#define HAVE_SIGNAL (0) +#define HAVE_SIGACTION (0) +#define HAVE_FORK (0) +#define HAVE_TLS_FUNC (0) +#define HAVE_TLS_VAR (0) + +#elif defined(_WIN32) + +#include <windows.h> +#include <signal.h> +static CRITICAL_SECTION bounds_sem; +#define INIT_SEM() InitializeCriticalSection(&bounds_sem) +#define EXIT_SEM() DeleteCriticalSection(&bounds_sem) +#define WAIT_SEM() EnterCriticalSection(&bounds_sem) +#define POST_SEM() LeaveCriticalSection(&bounds_sem) +#define TRY_SEM() TryEnterCriticalSection(&bounds_sem) +#define HAVE_MEMALIGN (0) +#define MALLOC_REDIR (0) +#define HAVE_PTHREAD_CREATE (0) +#define HAVE_CTYPE (0) +#define HAVE_ERRNO (0) +#define HAVE_SIGNAL (1) +#define HAVE_SIGACTION (0) +#define HAVE_FORK (0) +#define HAVE_TLS_FUNC (1) +#define HAVE_TLS_VAR (0) + +#else + +#define __USE_GNU /* get RTLD_NEXT */ +#include <sys/mman.h> +#include <ctype.h> +#include <pthread.h> +#include <dlfcn.h> +#include <errno.h> +#include <signal.h> +#ifdef __APPLE__ +#include <dispatch/dispatch.h> +static dispatch_semaphore_t bounds_sem; +#define INIT_SEM() bounds_sem = dispatch_semaphore_create(1) +#define EXIT_SEM() dispatch_release(*(dispatch_object_t*)&bounds_sem) +#define WAIT_SEM() if (use_sem) dispatch_semaphore_wait(bounds_sem, DISPATCH_TIME_FOREVER) +#define POST_SEM() if (use_sem) dispatch_semaphore_signal(bounds_sem) +#define TRY_SEM() if (use_sem) dispatch_semaphore_wait(bounds_sem, DISPATCH_TIME_NOW) +#elif 0 +#include <semaphore.h> +static sem_t bounds_sem; +#define INIT_SEM() sem_init (&bounds_sem, 0, 1) +#define EXIT_SEM() sem_destroy (&bounds_sem) +#define WAIT_SEM() if (use_sem) while (sem_wait (&bounds_sem) < 0 \ + && errno == EINTR) +#define POST_SEM() if (use_sem) sem_post (&bounds_sem) +#define TRY_SEM() if (use_sem) while (sem_trywait (&bounds_sem) < 0 \ + && errno == EINTR) +#elif 0 +static pthread_mutex_t bounds_mtx; +#define INIT_SEM() pthread_mutex_init (&bounds_mtx, NULL) +#define EXIT_SEM() pthread_mutex_destroy (&bounds_mtx) +#define WAIT_SEM() if (use_sem) pthread_mutex_lock (&bounds_mtx) +#define POST_SEM() if (use_sem) pthread_mutex_unlock (&bounds_mtx) +#define TRY_SEM() if (use_sem) pthread_mutex_trylock (&bounds_mtx) +#else +static pthread_spinlock_t bounds_spin; +/* about 25% faster then semaphore. */ +#define INIT_SEM() pthread_spin_init (&bounds_spin, 0) +#define EXIT_SEM() pthread_spin_destroy (&bounds_spin) +#define WAIT_SEM() if (use_sem) pthread_spin_lock (&bounds_spin) +#define POST_SEM() if (use_sem) pthread_spin_unlock (&bounds_spin) +#define TRY_SEM() if (use_sem) pthread_spin_trylock (&bounds_spin) +#endif +#define HAVE_MEMALIGN (1) +#define MALLOC_REDIR (1) +#define HAVE_PTHREAD_CREATE (1) +#define HAVE_CTYPE (1) +#define HAVE_ERRNO (1) +#define HAVE_SIGNAL (1) +#define HAVE_SIGACTION (1) +#define HAVE_FORK (1) +#if !defined(__APPLE__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define HAVE_TLS_FUNC (0) +#define HAVE_TLS_VAR (1) +#else +#define HAVE_TLS_FUNC (1) +#define HAVE_TLS_VAR (0) +#endif +#if defined TCC_MUSL || defined __ANDROID__ +# undef HAVE_CTYPE +#endif +#endif + +#if MALLOC_REDIR +static void *(*malloc_redir) (size_t); +static void *(*calloc_redir) (size_t, size_t); +static void (*free_redir) (void *); +static void *(*realloc_redir) (void *, size_t); +static unsigned int pool_index; +static unsigned char __attribute__((aligned(16))) initial_pool[256]; +#endif +#if HAVE_MEMALIGN +static void *(*memalign_redir) (size_t, size_t); +#endif +#if HAVE_PTHREAD_CREATE +static int (*pthread_create_redir) (pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); +#endif +#if HAVE_SIGNAL +typedef void (*bound_sig)(int); +static bound_sig (*signal_redir) (int signum, bound_sig handler); +#endif +#if HAVE_SIGACTION +static int (*sigaction_redir) (int signum, const struct sigaction *act, + struct sigaction *oldact); +#endif +#if HAVE_FORK +static int (*fork_redir) (void); +#endif + +#define TCC_TYPE_NONE (0) +#define TCC_TYPE_MALLOC (1) +#define TCC_TYPE_CALLOC (2) +#define TCC_TYPE_REALLOC (3) +#define TCC_TYPE_MEMALIGN (4) +#define TCC_TYPE_STRDUP (5) + +/* this pointer is generated when bound check is incorrect */ +#define INVALID_POINTER ((void *)(-2)) + +typedef struct tree_node Tree; +struct tree_node { + Tree * left, * right; + size_t start; + size_t size; + unsigned char type; + unsigned char is_invalid; /* true if pointers outside region are invalid */ +}; + +typedef struct alloca_list_struct { + size_t fp; + void *p; + size_t size; + struct alloca_list_struct *next; +} alloca_list_type; + +#if defined(_WIN32) +#define BOUND_TID_TYPE DWORD +#define BOUND_GET_TID(id) id = GetCurrentThreadId() +#elif defined(__OpenBSD__) +#define BOUND_TID_TYPE pid_t +#define BOUND_GET_TID(id) id = getthrid() +#elif defined(__FreeBSD__) +#define BOUND_TID_TYPE pid_t +#define BOUND_GET_TID(id) syscall (SYS_thr_self, &id) +#elif defined(__NetBSD__) +#define BOUND_TID_TYPE pid_t +#define BOUND_GET_TID(id) id = syscall (SYS__lwp_self) +#elif defined(__linux__) +#define BOUND_TID_TYPE pid_t +#define BOUND_GET_TID(id) id = syscall (SYS_gettid) +#else +#define BOUND_TID_TYPE int +#define BOUND_GET_TID(id) id = 0 +#endif + +typedef struct jmp_list_struct { + void *penv; + size_t fp; + size_t end_fp; + BOUND_TID_TYPE tid; + struct jmp_list_struct *next; +} jmp_list_type; + +#define BOUND_STATISTIC_SPLAY (0) +static Tree * splay (size_t addr, Tree *t); +static Tree * splay_end (size_t addr, Tree *t); +static Tree * splay_insert(size_t addr, size_t size, Tree * t); +static Tree * splay_delete(size_t addr, Tree *t); +void splay_printtree(Tree * t, int d); + +/* external interface */ +void __bounds_checking (int no_check); +void __bound_checking_lock (void); +void __bound_checking_unlock (void); +void __bound_never_fatal (int no_check); +DLL_EXPORT void * __bound_ptr_add(void *p, size_t offset); +DLL_EXPORT void * __bound_ptr_indir1(void *p, size_t offset); +DLL_EXPORT void * __bound_ptr_indir2(void *p, size_t offset); +DLL_EXPORT void * __bound_ptr_indir4(void *p, size_t offset); +DLL_EXPORT void * __bound_ptr_indir8(void *p, size_t offset); +DLL_EXPORT void * __bound_ptr_indir12(void *p, size_t offset); +DLL_EXPORT void * __bound_ptr_indir16(void *p, size_t offset); +DLL_EXPORT void FASTCALL __bound_local_new(void *p1); +DLL_EXPORT void FASTCALL __bound_local_delete(void *p1); +void __bound_init(size_t *, int); +void __bound_main_arg(int argc, char **argv, char **envp); +void __bound_exit(void); +void __bound_exit_dll(size_t *); +#if !defined(_WIN32) +void *__bound_mmap (void *start, size_t size, int prot, int flags, int fd, + off_t offset); +int __bound_munmap (void *start, size_t size); +DLL_EXPORT void __bound_siglongjmp(jmp_buf env, int val); +#endif +DLL_EXPORT void __bound_new_region(void *p, size_t size); +DLL_EXPORT void __bound_setjmp(jmp_buf env); +DLL_EXPORT void __bound_longjmp(jmp_buf env, int val); +DLL_EXPORT void *__bound_memcpy(void *dst, const void *src, size_t size); +DLL_EXPORT int __bound_memcmp(const void *s1, const void *s2, size_t size); +DLL_EXPORT void *__bound_memmove(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound_memset(void *dst, int c, size_t size); +DLL_EXPORT int __bound_strlen(const char *s); +DLL_EXPORT char *__bound_strcpy(char *dst, const char *src); +DLL_EXPORT char *__bound_strncpy(char *dst, const char *src, size_t n); +DLL_EXPORT int __bound_strcmp(const char *s1, const char *s2); +DLL_EXPORT int __bound_strncmp(const char *s1, const char *s2, size_t n); +DLL_EXPORT char *__bound_strcat(char *dest, const char *src); +DLL_EXPORT char *__bound_strncat(char *dest, const char *src, size_t n); +DLL_EXPORT char *__bound_strchr(const char *string, int ch); +DLL_EXPORT char *__bound_strrchr(const char *string, int ch); +DLL_EXPORT char *__bound_strdup(const char *s); + +#if defined(__arm__) && defined(__ARM_EABI__) +DLL_EXPORT void *__bound___aeabi_memcpy(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memmove(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memmove4(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memmove8(void *dst, const void *src, size_t size); +DLL_EXPORT void *__bound___aeabi_memset(void *dst, int c, size_t size); +DLL_EXPORT void *__aeabi_memcpy(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memmove(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memmove4(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memmove8(void *dst, const void *src, size_t size); +DLL_EXPORT void *__aeabi_memset(void *dst, int c, size_t size); +#endif + +#if MALLOC_REDIR +#define BOUND_MALLOC(a) malloc_redir(a) +#define BOUND_MEMALIGN(a,b) memalign_redir(a,b) +#define BOUND_FREE(a) free_redir(a) +#define BOUND_REALLOC(a,b) realloc_redir(a,b) +#define BOUND_CALLOC(a,b) calloc_redir(a,b) +#else +#define BOUND_MALLOC(a) malloc(a) +#define BOUND_MEMALIGN(a,b) memalign(a,b) +#define BOUND_FREE(a) free(a) +#define BOUND_REALLOC(a,b) realloc(a,b) +#define BOUND_CALLOC(a,b) calloc(a,b) +DLL_EXPORT void *__bound_malloc(size_t size, const void *caller); +DLL_EXPORT void *__bound_memalign(size_t size, size_t align, const void *caller); +DLL_EXPORT void __bound_free(void *ptr, const void *caller); +DLL_EXPORT void *__bound_realloc(void *ptr, size_t size, const void *caller); +DLL_EXPORT void *__bound_calloc(size_t nmemb, size_t size); +#endif + +#define FREE_REUSE_SIZE (100) +static unsigned int free_reuse_index; +static void *free_reuse_list[FREE_REUSE_SIZE]; + +static Tree *tree = NULL; +#define TREE_REUSE (1) +#if TREE_REUSE +static Tree *tree_free_list; +#endif +static alloca_list_type *alloca_list; +static jmp_list_type *jmp_list; + +static unsigned char inited; +static unsigned char print_warn_ptr_add; +static unsigned char print_calls; +static unsigned char print_heap; +static unsigned char print_statistic; +static unsigned char no_strdup; +static unsigned char use_sem; +static int never_fatal; +#if HAVE_TLS_FUNC +#if defined(_WIN32) +static int no_checking = 0; +static DWORD no_checking_key; +#define NO_CHECKING_CHECK() if (!p) { \ + p = (int *) LocalAlloc(LPTR, sizeof(int)); \ + if (!p) bound_alloc_error("tls malloc"); \ + *p = 0; \ + TlsSetValue(no_checking_key, p); \ + } +#define NO_CHECKING_GET() ({ int *p = TlsGetValue(no_checking_key); \ + NO_CHECKING_CHECK(); \ + *p; \ + }) +#define NO_CHECKING_SET(v) { int *p = TlsGetValue(no_checking_key); \ + NO_CHECKING_CHECK(); \ + *p = v; \ + } +#else +static int no_checking = 0; +static pthread_key_t no_checking_key; +#define NO_CHECKING_CHECK() if (!p) { \ + p = (int *) BOUND_MALLOC(sizeof(int)); \ + if (!p) bound_alloc_error("tls malloc"); \ + *p = 0; \ + pthread_setspecific(no_checking_key, p); \ + } +#define NO_CHECKING_GET() ({ int *p = pthread_getspecific(no_checking_key); \ + NO_CHECKING_CHECK(); \ + *p; \ + }) +#define NO_CHECKING_SET(v) { int *p = pthread_getspecific(no_checking_key); \ + NO_CHECKING_CHECK(); \ + *p = v; \ + } +#endif +#elif HAVE_TLS_VAR +static __thread int no_checking = 0; +#define NO_CHECKING_GET() no_checking +#define NO_CHECKING_SET(v) no_checking = v +#else +static int no_checking = 0; +#define NO_CHECKING_GET() no_checking +#define NO_CHECKING_SET(v) no_checking = v +#endif +static char exec[100]; + +#if BOUND_STATISTIC +static unsigned long long bound_ptr_add_count; +static unsigned long long bound_ptr_indir1_count; +static unsigned long long bound_ptr_indir2_count; +static unsigned long long bound_ptr_indir4_count; +static unsigned long long bound_ptr_indir8_count; +static unsigned long long bound_ptr_indir12_count; +static unsigned long long bound_ptr_indir16_count; +static unsigned long long bound_local_new_count; +static unsigned long long bound_local_delete_count; +static unsigned long long bound_malloc_count; +static unsigned long long bound_calloc_count; +static unsigned long long bound_realloc_count; +static unsigned long long bound_free_count; +static unsigned long long bound_memalign_count; +static unsigned long long bound_mmap_count; +static unsigned long long bound_munmap_count; +static unsigned long long bound_alloca_count; +static unsigned long long bound_setjmp_count; +static unsigned long long bound_longjmp_count; +static unsigned long long bound_mempcy_count; +static unsigned long long bound_memcmp_count; +static unsigned long long bound_memmove_count; +static unsigned long long bound_memset_count; +static unsigned long long bound_strlen_count; +static unsigned long long bound_strcpy_count; +static unsigned long long bound_strncpy_count; +static unsigned long long bound_strcmp_count; +static unsigned long long bound_strncmp_count; +static unsigned long long bound_strcat_count; +static unsigned long long bound_strncat_count; +static unsigned long long bound_strchr_count; +static unsigned long long bound_strrchr_count; +static unsigned long long bound_strdup_count; +static unsigned long long bound_not_found; +#define INCR_COUNT(x) ++x +#else +#define INCR_COUNT(x) +#endif +#if BOUND_STATISTIC_SPLAY +static unsigned long long bound_splay; +static unsigned long long bound_splay_end; +static unsigned long long bound_splay_insert; +static unsigned long long bound_splay_delete; +#define INCR_COUNT_SPLAY(x) ++x +#else +#define INCR_COUNT_SPLAY(x) +#endif + +int tcc_backtrace(const char *fmt, ...); + +/* print a bound error message */ +#define bound_warning(...) \ + do { \ + WAIT_SEM (); \ + tcc_backtrace("^bcheck.c^BCHECK: " __VA_ARGS__); \ + POST_SEM (); \ + } while (0) + +#define bound_error(...) \ + do { \ + bound_warning(__VA_ARGS__); \ + if (never_fatal == 0) \ + exit(255); \ + } while (0) + +static void bound_alloc_error(const char *s) +{ + fprintf(stderr,"FATAL: %s\n",s); + exit (1); +} + +static void bound_not_found_warning(const char *file, const char *function, + void *ptr) +{ + dprintf(stderr, "%s%s, %s(): Not found %p\n", exec, file, function, ptr); +} + +static void fetch_and_add(int* variable, int value) +{ +#if defined __i386__ || defined __x86_64__ + __asm__ volatile("lock; addl %0, %1" + : "+r" (value), "+m" (*variable) // input+output + : // No input-only + : "memory" + ); +#elif defined __arm__ + extern void fetch_and_add_arm(int* variable, int value); + fetch_and_add_arm(variable, value); +#elif defined __aarch64__ + extern void fetch_and_add_arm64(int* variable, int value); + fetch_and_add_arm64(variable, value); +#elif defined __riscv + extern void fetch_and_add_riscv64(int* variable, int value); + fetch_and_add_riscv64(variable, value); +#else + *variable += value; +#endif +} + +/* enable/disable checking. This can be used in signal handlers. */ +void __bounds_checking (int no_check) +{ +#if HAVE_TLS_FUNC || HAVE_TLS_VAR + NO_CHECKING_SET(NO_CHECKING_GET() + no_check); +#else + fetch_and_add (&no_checking, no_check); +#endif +} + +void __bound_checking_lock(void) +{ + WAIT_SEM (); +} + +void __bound_checking_unlock(void) +{ + POST_SEM (); +} + +/* enable/disable checking. This can be used in signal handlers. */ +void __bound_never_fatal (int neverfatal) +{ + fetch_and_add (&never_fatal, neverfatal); +} + +/* return '(p + offset)' for pointer arithmetic (a pointer can reach + the end of a region in this case */ +void * __bound_ptr_add(void *p, size_t offset) +{ + size_t addr = (size_t)p; + + if (NO_CHECKING_GET()) + return p + offset; + + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, p, (unsigned long)offset); + + WAIT_SEM (); + INCR_COUNT(bound_ptr_add_count); + if (tree) { + addr -= tree->start; + if (addr >= tree->size) { + addr = (size_t)p; + tree = splay (addr, tree); + addr -= tree->start; + } + if (addr >= tree->size) { + addr = (size_t)p; + tree = splay_end (addr, tree); + addr -= tree->start; + } + if (addr <= tree->size) { + if (tree->is_invalid || addr + offset > tree->size) { + POST_SEM (); + if (print_warn_ptr_add) + bound_warning("%p is outside of the region", p + offset); + if (never_fatal <= 0) + return INVALID_POINTER; /* return an invalid pointer */ + return p + offset; + } + } + else if (p) { /* Allow NULL + offset. offsetoff is using it. */ + INCR_COUNT(bound_not_found); + POST_SEM (); + bound_not_found_warning (__FILE__, __FUNCTION__, p); + return p + offset; + } + } + POST_SEM (); + return p + offset; +} + +/* return '(p + offset)' for pointer indirection (the resulting must + be strictly inside the region */ +#define BOUND_PTR_INDIR(dsize) \ +void * __bound_ptr_indir ## dsize (void *p, size_t offset) \ +{ \ + size_t addr = (size_t)p; \ + \ + if (NO_CHECKING_GET()) \ + return p + offset; \ + \ + dprintf(stderr, "%s, %s(): %p 0x%lx\n", \ + __FILE__, __FUNCTION__, p, (unsigned long)offset); \ + WAIT_SEM (); \ + INCR_COUNT(bound_ptr_indir ## dsize ## _count); \ + if (tree) { \ + addr -= tree->start; \ + if (addr >= tree->size) { \ + addr = (size_t)p; \ + tree = splay (addr, tree); \ + addr -= tree->start; \ + } \ + if (addr >= tree->size) { \ + addr = (size_t)p; \ + tree = splay_end (addr, tree); \ + addr -= tree->start; \ + } \ + if (addr <= tree->size) { \ + if (tree->is_invalid || addr + offset + dsize > tree->size) { \ + POST_SEM (); \ + bound_warning("%p is outside of the region", p + offset); \ + if (never_fatal <= 0) \ + return INVALID_POINTER; /* return an invalid pointer */ \ + return p + offset; \ + } \ + } \ + else { \ + INCR_COUNT(bound_not_found); \ + POST_SEM (); \ + bound_not_found_warning (__FILE__, __FUNCTION__, p); \ + return p + offset; \ + } \ + } \ + POST_SEM (); \ + return p + offset; \ +} + +BOUND_PTR_INDIR(1) +BOUND_PTR_INDIR(2) +BOUND_PTR_INDIR(4) +BOUND_PTR_INDIR(8) +BOUND_PTR_INDIR(12) +BOUND_PTR_INDIR(16) + +/* Needed when using ...libtcc1-usegcc=yes in lib/Makefile */ +#if (defined(__GNUC__) && (__GNUC__ >= 6)) || defined(__clang__) +/* + * At least gcc 6.2 complains when __builtin_frame_address is used with + * nonzero argument. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wframe-address" +#endif + +/* return the frame pointer of the caller */ +#define GET_CALLER_FP(fp)\ +{\ + fp = (size_t)__builtin_frame_address(1);\ +} + +/* called when entering a function to add all the local regions */ +void FASTCALL __bound_local_new(void *p1) +{ + size_t addr, fp, *p = p1; + + if (NO_CHECKING_GET()) + return; + GET_CALLER_FP(fp); + dprintf(stderr, "%s, %s(): p1=%p fp=%p\n", + __FILE__, __FUNCTION__, p, (void *)fp); + WAIT_SEM (); + while ((addr = p[0])) { + INCR_COUNT(bound_local_new_count); + tree = splay_insert(addr + fp, p[1], tree); + p += 2; + } + POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = p1; + while ((addr = p[0])) { + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) (addr + fp), (unsigned long) p[1]); + p += 2; + } + } +#endif +} + +/* called when leaving a function to delete all the local regions */ +void FASTCALL __bound_local_delete(void *p1) +{ + size_t addr, fp, *p = p1; + + if (NO_CHECKING_GET()) + return; + GET_CALLER_FP(fp); + dprintf(stderr, "%s, %s(): p1=%p fp=%p\n", + __FILE__, __FUNCTION__, p, (void *)fp); + WAIT_SEM (); + while ((addr = p[0])) { + INCR_COUNT(bound_local_delete_count); + tree = splay_delete(addr + fp, tree); + p += 2; + } + if (alloca_list) { + alloca_list_type *last = NULL; + alloca_list_type *cur = alloca_list; + + do { + if (cur->fp == fp) { + if (last) + last->next = cur->next; + else + alloca_list = cur->next; + tree = splay_delete ((size_t) cur->p, tree); + dprintf(stderr, "%s, %s(): remove alloca/vla %p\n", + __FILE__, __FUNCTION__, cur->p); + BOUND_FREE (cur); + cur = last ? last->next : alloca_list; + } + else { + last = cur; + cur = cur->next; + } + } while (cur); + } + if (jmp_list) { + jmp_list_type *last = NULL; + jmp_list_type *cur = jmp_list; + + do { + if (cur->fp == fp) { + if (last) + last->next = cur->next; + else + jmp_list = cur->next; + dprintf(stderr, "%s, %s(): remove setjmp %p\n", + __FILE__, __FUNCTION__, cur->penv); + BOUND_FREE (cur); + cur = last ? last->next : jmp_list; + } + else { + last = cur; + cur = cur->next; + } + } while (cur); + } + + POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = p1; + while ((addr = p[0])) { + if (addr != 1) { + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) (addr + fp), (unsigned long) p[1]); + } + p+= 2; + } + } +#endif +} + +/* used by alloca */ +void __bound_new_region(void *p, size_t size) +{ + size_t fp; + alloca_list_type *last; + alloca_list_type *cur; + alloca_list_type *new; + + if (NO_CHECKING_GET()) + return; + + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, p, (unsigned long)size); + GET_CALLER_FP (fp); + new = BOUND_MALLOC (sizeof (alloca_list_type)); + WAIT_SEM (); + INCR_COUNT(bound_alloca_count); + last = NULL; + cur = alloca_list; + while (cur) { +#if defined(__i386__) || (defined(__arm__) && !defined(__ARM_EABI__)) + int align = 4; +#elif defined(__arm__) + int align = 8; +#else + int align = 16; +#endif + void *cure = (void *)((char *)cur->p + ((cur->size + align) & -align)); + void *pe = (void *)((char *)p + ((size + align) & -align)); + if (cur->fp == fp && ((cur->p <= p && cure > p) || + (p <= cur->p && pe > cur->p))) { + if (last) + last->next = cur->next; + else + alloca_list = cur->next; + tree = splay_delete((size_t)cur->p, tree); + break; + } + last = cur; + cur = cur->next; + } + tree = splay_insert((size_t)p, size, tree); + if (new) { + new->fp = fp; + new->p = p; + new->size = size; + new->next = alloca_list; + alloca_list = new; + } + POST_SEM (); + if (cur) { + dprintf(stderr, "%s, %s(): remove alloca/vla %p\n", + __FILE__, __FUNCTION__, cur->p); + BOUND_FREE (cur); + } +} + +void __bound_setjmp(jmp_buf env) +{ + jmp_list_type *jl; + void *e = (void *) env; + + if (NO_CHECKING_GET() == 0) { + dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, e); + WAIT_SEM (); + INCR_COUNT(bound_setjmp_count); + jl = jmp_list; + while (jl) { + if (jl->penv == e) + break; + jl = jl->next; + } + if (jl == NULL) { + jl = BOUND_MALLOC (sizeof (jmp_list_type)); + if (jl) { + jl->penv = e; + jl->next = jmp_list; + jmp_list = jl; + } + } + if (jl) { + size_t fp; + + GET_CALLER_FP (fp); + jl->fp = fp; + jl->end_fp = (size_t)__builtin_frame_address(0); + BOUND_GET_TID(jl->tid); + } + POST_SEM (); + } +} + +static void __bound_long_jump(jmp_buf env, int val, int sig, const char *func) +{ + jmp_list_type *jl; + void *e; + BOUND_TID_TYPE tid; + + if (NO_CHECKING_GET() == 0) { + e = (void *)env; + BOUND_GET_TID(tid); + dprintf(stderr, "%s, %s(): %p\n", __FILE__, func, e); + WAIT_SEM(); + INCR_COUNT(bound_longjmp_count); + jl = jmp_list; + while (jl) { + if (jl->penv == e && jl->tid == tid) { + size_t start_fp = (size_t)__builtin_frame_address(0); + size_t end_fp = jl->end_fp; + jmp_list_type *cur = jmp_list; + jmp_list_type *last = NULL; + + while (cur->penv != e || cur->tid != tid) { + if (cur->tid == tid) { + dprintf(stderr, "%s, %s(): remove setjmp %p\n", + __FILE__, func, cur->penv); + if (last) + last->next = cur->next; + else + jmp_list = cur->next; + BOUND_FREE (cur); + cur = last ? last->next : jmp_list; + } + else { + last = cur; + cur = cur->next; + } + } + for (;;) { + Tree *t = tree; + alloca_list_type *last; + alloca_list_type *cur; + + while (t && (t->start < start_fp || t->start > end_fp)) + if (t->start < start_fp) + t = t->right; + else + t = t->left; + if (t == NULL) + break; + last = NULL; + cur = alloca_list; + while (cur) { + if ((size_t) cur->p == t->start) { + dprintf(stderr, "%s, %s(): remove alloca/vla %p\n", + __FILE__, func, cur->p); + if (last) + last->next = cur->next; + else + alloca_list = cur->next; + BOUND_FREE (cur); + break; + } + last = cur; + cur = cur->next; + } + dprintf(stderr, "%s, %s(): delete %p\n", + __FILE__, func, (void *) t->start); + tree = splay_delete(t->start, tree); + } + break; + } + jl = jl->next; + } + POST_SEM(); + } +#if !defined(_WIN32) + sig ? siglongjmp(env, val) : +#endif + longjmp (env, val); +} + +void __bound_longjmp(jmp_buf env, int val) +{ + __bound_long_jump(env,val, 0, __FUNCTION__); +} + +#if !defined(_WIN32) +void __bound_siglongjmp(jmp_buf env, int val) +{ + __bound_long_jump(env,val, 1, __FUNCTION__); +} +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 6)) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +void __bound_init(size_t *p, int mode) +{ + dprintf(stderr, "%s, %s(): start %s\n", __FILE__, __FUNCTION__, + mode < 0 ? "lazy" : mode == 0 ? "normal use" : "for -run"); + + if (inited) { + WAIT_SEM(); + goto add_bounds; + } + inited = 1; + +#if HAVE_TLS_FUNC +#if defined(_WIN32) + no_checking_key = TlsAlloc(); + TlsSetValue(no_checking_key, &no_checking); +#else + pthread_key_create(&no_checking_key, NULL); + pthread_setspecific(no_checking_key, &no_checking); +#endif +#endif + NO_CHECKING_SET(1); + + print_warn_ptr_add = getenv ("TCC_BOUNDS_WARN_POINTER_ADD") != NULL; + print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL; + print_heap = getenv ("TCC_BOUNDS_PRINT_HEAP") != NULL; + print_statistic = getenv ("TCC_BOUNDS_PRINT_STATISTIC") != NULL; + never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL; + + INIT_SEM (); + +#if MALLOC_REDIR + { + void *addr = mode > 0 ? RTLD_DEFAULT : RTLD_NEXT; + + /* tcc -run required RTLD_DEFAULT. Normal usage requires RTLD_NEXT, + but using RTLD_NEXT with -run segfaults on MacOS in dyld as the + generated code segment isn't registered with dyld and hence the + caller image of dlsym isn't known to it */ + *(void **) (&malloc_redir) = dlsym (addr, "malloc"); + if (malloc_redir == NULL) { + dprintf(stderr, "%s, %s(): use RTLD_DEFAULT\n", + __FILE__, __FUNCTION__); + addr = RTLD_DEFAULT; + *(void **) (&malloc_redir) = dlsym (addr, "malloc"); + } + *(void **) (&calloc_redir) = dlsym (addr, "calloc"); + *(void **) (&free_redir) = dlsym (addr, "free"); + *(void **) (&realloc_redir) = dlsym (addr, "realloc"); + *(void **) (&memalign_redir) = dlsym (addr, "memalign"); + dprintf(stderr, "%s, %s(): malloc_redir %p\n", + __FILE__, __FUNCTION__, malloc_redir); + dprintf(stderr, "%s, %s(): free_redir %p\n", + __FILE__, __FUNCTION__, free_redir); + dprintf(stderr, "%s, %s(): realloc_redir %p\n", + __FILE__, __FUNCTION__, realloc_redir); + dprintf(stderr, "%s, %s(): memalign_redir %p\n", + __FILE__, __FUNCTION__, memalign_redir); + if (malloc_redir == NULL || free_redir == NULL) + bound_alloc_error ("Cannot redirect malloc/free"); +#if HAVE_PTHREAD_CREATE + *(void **) (&pthread_create_redir) = dlsym (addr, "pthread_create"); + dprintf(stderr, "%s, %s(): pthread_create_redir %p\n", + __FILE__, __FUNCTION__, pthread_create_redir); + if (pthread_create_redir == NULL) + bound_alloc_error ("Cannot redirect pthread_create"); +#endif +#if HAVE_SIGNAL + *(void **) (&signal_redir) = dlsym (addr, "signal"); + dprintf(stderr, "%s, %s(): signal_redir %p\n", + __FILE__, __FUNCTION__, signal_redir); + if (signal_redir == NULL) + bound_alloc_error ("Cannot redirect signal"); +#endif +#if HAVE_SIGACTION + *(void **) (&sigaction_redir) = dlsym (addr, "sigaction"); + dprintf(stderr, "%s, %s(): sigaction_redir %p\n", + __FILE__, __FUNCTION__, sigaction_redir); + if (sigaction_redir == NULL) + bound_alloc_error ("Cannot redirect sigaction"); +#endif +#if HAVE_FORK + *(void **) (&fork_redir) = dlsym (addr, "fork"); + dprintf(stderr, "%s, %s(): fork_redir %p\n", + __FILE__, __FUNCTION__, fork_redir); + if (fork_redir == NULL) + bound_alloc_error ("Cannot redirect fork"); +#endif + } +#endif + +#ifdef __linux__ + { + FILE *fp; + unsigned char found; + unsigned long start; + unsigned long end; + unsigned long ad = + (unsigned long) __builtin_return_address(0); + char line[1000]; + + /* Display exec name. Usefull when a lot of code is compiled with tcc */ + fp = fopen ("/proc/self/comm", "r"); + if (fp) { + memset (exec, 0, sizeof(exec)); + fread (exec, 1, sizeof(exec) - 2, fp); + if (strchr(exec,'\n')) + *strchr(exec,'\n') = '\0'; + strcat (exec, ":"); + fclose (fp); + } + /* check if dlopen is used (is threre a better way?) */ + found = 0; + fp = fopen ("/proc/self/maps", "r"); + if (fp) { + while (fgets (line, sizeof(line), fp)) { + if (sscanf (line, "%lx-%lx", &start, &end) == 2 && + ad >= start && ad < end) { + found = 1; + break; + } + if (strstr (line,"[heap]")) + break; + } + fclose (fp); + } + if (found == 0) { + use_sem = 1; + no_strdup = 1; + } + } +#endif + + WAIT_SEM (); + +#if HAVE_CTYPE +#ifdef __APPLE__ + tree = splay_insert((size_t) &_DefaultRuneLocale, + sizeof (_DefaultRuneLocale), tree); +#else + /* XXX: Does not work if locale is changed */ + tree = splay_insert((size_t) __ctype_b_loc(), + sizeof (unsigned short *), tree); + tree = splay_insert((size_t) (*__ctype_b_loc() - 128), + 384 * sizeof (unsigned short), tree); + tree = splay_insert((size_t) __ctype_tolower_loc(), + sizeof (__int32_t *), tree); + tree = splay_insert((size_t) (*__ctype_tolower_loc() - 128), + 384 * sizeof (__int32_t), tree); + tree = splay_insert((size_t) __ctype_toupper_loc(), + sizeof (__int32_t *), tree); + tree = splay_insert((size_t) (*__ctype_toupper_loc() - 128), + 384 * sizeof (__int32_t), tree); +#endif +#endif +#if HAVE_ERRNO + tree = splay_insert((size_t) (&errno), sizeof (int), tree); +#endif + +add_bounds: + if (!p) + goto no_bounds; + + /* add all static bound check values */ + while (p[0] != 0) { + tree = splay_insert(p[0], p[1], tree); +#if BOUND_DEBUG + if (print_calls) { + dprintf(stderr, "%s, %s(): static var %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) p[0], (unsigned long) p[1]); + } +#endif + p += 2; + } +no_bounds: + + POST_SEM (); + NO_CHECKING_SET(0); + dprintf(stderr, "%s, %s(): end\n\n", __FILE__, __FUNCTION__); +} + +void +#if (defined(__GLIBC__) && (__GLIBC_MINOR__ >= 4)) || defined(_WIN32) +__attribute__((constructor)) +#endif +__bound_main_arg(int argc, char **argv, char **envp) +{ + __bound_init (0, -1); + if (argc && argv) { + int i; + + WAIT_SEM (); + for (i = 0; i < argc; i++) + tree = splay_insert((size_t) argv[i], strlen (argv[i]) + 1, tree); + tree = splay_insert((size_t) argv, (argc + 1) * sizeof(char *), tree); + POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + for (i = 0; i < argc; i++) + dprintf(stderr, "%s, %s(): arg %p 0x%lx\n", + __FILE__, __FUNCTION__, + argv[i], (unsigned long)(strlen (argv[i]) + 1)); + dprintf(stderr, "%s, %s(): argv %p %d\n", + __FILE__, __FUNCTION__, argv, + (int)((argc + 1) * sizeof(char *))); + } +#endif + } + + if (envp && *envp) { + char **p = envp; + + WAIT_SEM (); + while (*p) { + tree = splay_insert((size_t) *p, strlen (*p) + 1, tree); + ++p; + } + tree = splay_insert((size_t) envp, (++p - envp) * sizeof(char *), tree); + POST_SEM (); +#if BOUND_DEBUG + if (print_calls) { + p = envp; + while (*p) { + dprintf(stderr, "%s, %s(): env %p 0x%lx\n", + __FILE__, __FUNCTION__, + *p, (unsigned long)(strlen (*p) + 1)); + ++p; + } + dprintf(stderr, "%s, %s(): environ %p %d\n", + __FILE__, __FUNCTION__, envp, + (int)((++p - envp) * sizeof(char *))); + } +#endif + } +} + +void __attribute__((destructor)) __bound_exit(void) +{ + int i; + static const char * const alloc_type[] = { + "", "malloc", "calloc", "realloc", "memalign", "strdup" + }; + + dprintf(stderr, "%s, %s():\n", __FILE__, __FUNCTION__); + + if (inited) { +#if !defined(_WIN32) && !defined(__APPLE__) && !defined TCC_MUSL && \ + !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \ + !defined(__ANDROID__) + if (print_heap) { + extern void __libc_freeres (void); + __libc_freeres (); + } +#endif + + NO_CHECKING_SET(1); + + TRY_SEM (); + while (alloca_list) { + alloca_list_type *next = alloca_list->next; + + tree = splay_delete ((size_t) alloca_list->p, tree); + BOUND_FREE (alloca_list); + alloca_list = next; + } + while (jmp_list) { + jmp_list_type *next = jmp_list->next; + + BOUND_FREE (jmp_list); + jmp_list = next; + } + for (i = 0; i < FREE_REUSE_SIZE; i++) { + if (free_reuse_list[i]) { + tree = splay_delete ((size_t) free_reuse_list[i], tree); + BOUND_FREE (free_reuse_list[i]); + } + } + while (tree) { + if (print_heap && tree->type != 0) + fprintf (stderr, "%s, %s(): %s found size %lu\n", + __FILE__, __FUNCTION__, alloc_type[tree->type], + (unsigned long) tree->size); + tree = splay_delete (tree->start, tree); + } +#if TREE_REUSE + while (tree_free_list) { + Tree *next = tree_free_list->left; + BOUND_FREE (tree_free_list); + tree_free_list = next; + } +#endif + POST_SEM (); + EXIT_SEM (); +#if HAVE_TLS_FUNC +#if defined(_WIN32) + TlsFree(no_checking_key); +#else + pthread_key_delete(no_checking_key); +#endif +#endif + inited = 0; + if (print_statistic) { +#if BOUND_STATISTIC + fprintf (stderr, "bound_ptr_add_count %llu\n", bound_ptr_add_count); + fprintf (stderr, "bound_ptr_indir1_count %llu\n", bound_ptr_indir1_count); + fprintf (stderr, "bound_ptr_indir2_count %llu\n", bound_ptr_indir2_count); + fprintf (stderr, "bound_ptr_indir4_count %llu\n", bound_ptr_indir4_count); + fprintf (stderr, "bound_ptr_indir8_count %llu\n", bound_ptr_indir8_count); + fprintf (stderr, "bound_ptr_indir12_count %llu\n", bound_ptr_indir12_count); + fprintf (stderr, "bound_ptr_indir16_count %llu\n", bound_ptr_indir16_count); + fprintf (stderr, "bound_local_new_count %llu\n", bound_local_new_count); + fprintf (stderr, "bound_local_delete_count %llu\n", bound_local_delete_count); + fprintf (stderr, "bound_malloc_count %llu\n", bound_malloc_count); + fprintf (stderr, "bound_calloc_count %llu\n", bound_calloc_count); + fprintf (stderr, "bound_realloc_count %llu\n", bound_realloc_count); + fprintf (stderr, "bound_free_count %llu\n", bound_free_count); + fprintf (stderr, "bound_memalign_count %llu\n", bound_memalign_count); + fprintf (stderr, "bound_mmap_count %llu\n", bound_mmap_count); + fprintf (stderr, "bound_munmap_count %llu\n", bound_munmap_count); + fprintf (stderr, "bound_alloca_count %llu\n", bound_alloca_count); + fprintf (stderr, "bound_setjmp_count %llu\n", bound_setjmp_count); + fprintf (stderr, "bound_longjmp_count %llu\n", bound_longjmp_count); + fprintf (stderr, "bound_mempcy_count %llu\n", bound_mempcy_count); + fprintf (stderr, "bound_memcmp_count %llu\n", bound_memcmp_count); + fprintf (stderr, "bound_memmove_count %llu\n", bound_memmove_count); + fprintf (stderr, "bound_memset_count %llu\n", bound_memset_count); + fprintf (stderr, "bound_strlen_count %llu\n", bound_strlen_count); + fprintf (stderr, "bound_strcpy_count %llu\n", bound_strcpy_count); + fprintf (stderr, "bound_strncpy_count %llu\n", bound_strncpy_count); + fprintf (stderr, "bound_strcmp_count %llu\n", bound_strcmp_count); + fprintf (stderr, "bound_strncmp_count %llu\n", bound_strncmp_count); + fprintf (stderr, "bound_strcat_count %llu\n", bound_strcat_count); + fprintf (stderr, "bound_strncat_count %llu\n", bound_strncat_count); + fprintf (stderr, "bound_strchr_count %llu\n", bound_strchr_count); + fprintf (stderr, "bound_strrchr_count %llu\n", bound_strrchr_count); + fprintf (stderr, "bound_strdup_count %llu\n", bound_strdup_count); + fprintf (stderr, "bound_not_found %llu\n", bound_not_found); +#endif +#if BOUND_STATISTIC_SPLAY + fprintf (stderr, "bound_splay %llu\n", bound_splay); + fprintf (stderr, "bound_splay_end %llu\n", bound_splay_end); + fprintf (stderr, "bound_splay_insert %llu\n", bound_splay_insert); + fprintf (stderr, "bound_splay_delete %llu\n", bound_splay_delete); +#endif + } + } +} + +void __bound_exit_dll(size_t *p) +{ + dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); + + if (p) { + WAIT_SEM (); + while (p[0] != 0) { + tree = splay_delete(p[0], tree); +#if BOUND_DEBUG + if (print_calls) { + dprintf(stderr, "%s, %s(): remove static var %p 0x%lx\n", + __FILE__, __FUNCTION__, + (void *) p[0], (unsigned long) p[1]); + } +#endif + p += 2; + } + POST_SEM (); + } +} + +#if HAVE_PTHREAD_CREATE +typedef struct { + void *(*start_routine) (void *); + void *arg; + sigset_t old_mask; +} bound_thread_create_type; + +static void *bound_thread_create(void *bdata) +{ + bound_thread_create_type *data = (bound_thread_create_type *) bdata; + void *retval; +#if HAVE_TLS_FUNC + int *p = (int *) BOUND_MALLOC(sizeof(int)); + + if (!p) bound_alloc_error("bound_thread_create malloc"); + *p = 0; + pthread_setspecific(no_checking_key, p); +#endif + pthread_sigmask(SIG_SETMASK, &data->old_mask, NULL); + retval = data->start_routine(data->arg); +#if HAVE_TLS_FUNC + pthread_setspecific(no_checking_key, NULL); + BOUND_FREE (p); +#endif + BOUND_FREE (data); + return retval; +} + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + int retval; + bound_thread_create_type *data; + sigset_t mask; + sigset_t old_mask; + + use_sem = 1; + dprintf (stderr, "%s, %s()\n", __FILE__, __FUNCTION__); + sigfillset(&mask); + pthread_sigmask(SIG_SETMASK, &mask, &old_mask); + data = (bound_thread_create_type *) BOUND_MALLOC(sizeof(bound_thread_create_type)); + if (!data) bound_alloc_error("bound_thread_create malloc"); + data->start_routine = start_routine; + data->arg = arg; + data->old_mask = old_mask; + retval = pthread_create_redir(thread, attr, bound_thread_create, data); + pthread_sigmask(SIG_SETMASK, &old_mask, NULL); + return retval; +} +#endif + +#if HAVE_SIGNAL || HAVE_SIGACTION +typedef union { +#if HAVE_SIGNAL + bound_sig signal_handler; +#endif +#if HAVE_SIGACTION + void (*sig_handler)(int); + void (*sig_sigaction)(int, siginfo_t *, void *); +#endif +} bound_sig_type; + +static unsigned char bound_sig_used[NSIG]; +static bound_sig_type bound_sig_data[NSIG]; +#endif + +#if HAVE_SIGNAL +static void signal_handler(int sig) +{ + __bounds_checking(1); + bound_sig_data[sig].signal_handler(sig); + __bounds_checking(-1); +} + +bound_sig signal(int signum, bound_sig handler) +{ + bound_sig retval; + + dprintf (stderr, "%s, %s() %d %p\n", __FILE__, __FUNCTION__, + signum, handler); + retval = signal_redir(signum, handler ? signal_handler : handler); + if (retval != SIG_ERR) { + if (bound_sig_used[signum]) + retval = bound_sig_data[signum].signal_handler; + if (handler) { + bound_sig_used[signum] = 1; + bound_sig_data[signum].signal_handler = handler; + } + } + return retval; +} +#endif + +#if HAVE_SIGACTION +static void sig_handler(int sig) +{ + __bounds_checking(1); + bound_sig_data[sig].sig_handler(sig); + __bounds_checking(-1); +} + +static void sig_sigaction(int sig, siginfo_t *info, void *ucontext) +{ + __bounds_checking(1); + bound_sig_data[sig].sig_sigaction(sig, info, ucontext); + __bounds_checking(-1); +} + +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + int retval; + struct sigaction nact, oact; + + dprintf (stderr, "%s, %s() %d %p %p\n", __FILE__, __FUNCTION__, + signum, act, oldact); + + if (sigaction_redir == NULL) + __bound_init(0,-1); + + if (act) { + nact = *act; + if (nact.sa_flags & SA_SIGINFO) + nact.sa_sigaction = sig_sigaction; + else + nact.sa_handler = sig_handler; + retval = sigaction_redir(signum, &nact, &oact); + } + else + retval = sigaction_redir(signum, act, &oact); + if (retval >= 0) { + if (bound_sig_used[signum]) { + if (oact.sa_flags & SA_SIGINFO) + oact.sa_sigaction = bound_sig_data[signum].sig_sigaction; + else + oact.sa_handler = bound_sig_data[signum].sig_handler; + } + if (oldact) { + *oldact = oact; + } + if (act) { + bound_sig_used[signum] = 1; + if (act->sa_flags & SA_SIGINFO) + bound_sig_data[signum].sig_sigaction = act->sa_sigaction; + else + bound_sig_data[signum].sig_handler = act->sa_handler; + } + } + return retval; +} +#endif + +#if HAVE_FORK +pid_t fork(void) +{ + pid_t retval; + + WAIT_SEM(); + retval = (*fork_redir)(); + if (retval == 0) + INIT_SEM(); + else + POST_SEM(); + return retval; +} +#endif + +#if MALLOC_REDIR +void *malloc(size_t size) +#else +void *__bound_malloc(size_t size, const void *caller) +#endif +{ + void *ptr; + +#if MALLOC_REDIR + /* This will catch the first dlsym call from __bound_init */ + if (malloc_redir == NULL) { + __bound_init (0, -1); + if (malloc_redir == NULL) { + ptr = &initial_pool[pool_index]; + pool_index = (pool_index + size + 15) & ~15; + if (pool_index >= sizeof (initial_pool)) + bound_alloc_error ("initial memory pool too small"); + dprintf (stderr, "%s, %s(): initial %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + return ptr; + } + } +#endif + /* we allocate one more byte to ensure the regions will be + separated by at least one byte. With the glibc malloc, it may + be in fact not necessary */ + ptr = BOUND_MALLOC (size + 1); + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + + if (inited && NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_malloc_count); + + if (ptr) { + tree = splay_insert ((size_t) ptr, size ? size : size + 1, tree); + if (tree && tree->start == (size_t) ptr) + tree->type = TCC_TYPE_MALLOC; + } + POST_SEM (); + } + return ptr; +} + +#if MALLOC_REDIR +void *memalign(size_t size, size_t align) +#else +void *__bound_memalign(size_t size, size_t align, const void *caller) +#endif +{ + void *ptr; + +#if HAVE_MEMALIGN + /* we allocate one more byte to ensure the regions will be + separated by at least one byte. With the glibc malloc, it may + be in fact not necessary */ + ptr = BOUND_MEMALIGN(size + 1, align); +#else + if (align > 4) { + /* XXX: handle it ? */ + ptr = NULL; + } else { + /* we suppose that malloc aligns to at least four bytes */ + ptr = BOUND_MALLOC(size + 1); + } +#endif + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + + if (NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_memalign_count); + + if (ptr) { + tree = splay_insert((size_t) ptr, size ? size : size + 1, tree); + if (tree && tree->start == (size_t) ptr) + tree->type = TCC_TYPE_MEMALIGN; + } + POST_SEM (); + } + return ptr; +} + +#if MALLOC_REDIR +void free(void *ptr) +#else +void __bound_free(void *ptr, const void *caller) +#endif +{ + size_t addr = (size_t) ptr; + void *p; + + if (ptr == NULL || tree == NULL +#if MALLOC_REDIR + || ((unsigned char *) ptr >= &initial_pool[0] && + (unsigned char *) ptr < &initial_pool[sizeof(initial_pool)]) +#endif + ) + return; + + dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr); + + if (inited && NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_free_count); + tree = splay (addr, tree); + if (tree->start == addr) { + if (tree->is_invalid) { + POST_SEM (); + bound_error("freeing invalid region"); + return; + } + tree->is_invalid = 1; + memset (ptr, 0x5a, tree->size); + p = free_reuse_list[free_reuse_index]; + free_reuse_list[free_reuse_index] = ptr; + free_reuse_index = (free_reuse_index + 1) % FREE_REUSE_SIZE; + if (p) + tree = splay_delete((size_t)p, tree); + ptr = p; + } + POST_SEM (); + } + BOUND_FREE (ptr); +} + +#if MALLOC_REDIR +void *realloc(void *ptr, size_t size) +#else +void *__bound_realloc(void *ptr, size_t size, const void *caller) +#endif +{ + void *new_ptr; + + if (size == 0) { +#if MALLOC_REDIR + free(ptr); +#else + __bound_free(ptr, caller); +#endif + return NULL; + } + + new_ptr = BOUND_REALLOC (ptr, size + 1); + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, new_ptr, (unsigned long)size); + + if (NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_realloc_count); + + if (ptr) + tree = splay_delete ((size_t) ptr, tree); + if (new_ptr) { + tree = splay_insert ((size_t) new_ptr, size ? size : size + 1, tree); + if (tree && tree->start == (size_t) new_ptr) + tree->type = TCC_TYPE_REALLOC; + } + POST_SEM (); + } + return new_ptr; +} + +#if MALLOC_REDIR +void *calloc(size_t nmemb, size_t size) +#else +void *__bound_calloc(size_t nmemb, size_t size) +#endif +{ + void *ptr; + + size *= nmemb; +#if MALLOC_REDIR + /* This will catch the first dlsym call from __bound_init */ + if (malloc_redir == NULL) { + __bound_init (0, -1); + if (malloc_redir == NULL) { + ptr = &initial_pool[pool_index]; + pool_index = (pool_index + size + 15) & ~15; + if (pool_index >= sizeof (initial_pool)) + bound_alloc_error ("initial memory pool too small"); + dprintf (stderr, "%s, %s(): initial %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + memset (ptr, 0, size); + return ptr; + } + } +#endif + ptr = BOUND_MALLOC(size + 1); + dprintf (stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); + + if (ptr) { + memset (ptr, 0, size); + if (NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_calloc_count); + tree = splay_insert ((size_t) ptr, size ? size : size + 1, tree); + if (tree && tree->start == (size_t) ptr) + tree->type = TCC_TYPE_CALLOC; + POST_SEM (); + } + } + return ptr; +} + +#if !defined(_WIN32) +void *__bound_mmap (void *start, size_t size, int prot, + int flags, int fd, off_t offset) +{ + void *result; + + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, start, (unsigned long)size); + result = mmap (start, size, prot, flags, fd, offset); + if (result && NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_mmap_count); + tree = splay_insert((size_t)result, size, tree); + POST_SEM (); + } + return result; +} + +int __bound_munmap (void *start, size_t size) +{ + int result; + + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, start, (unsigned long)size); + if (start && NO_CHECKING_GET() == 0) { + WAIT_SEM (); + INCR_COUNT(bound_munmap_count); + tree = splay_delete ((size_t) start, tree); + POST_SEM (); + } + result = munmap (start, size); + return result; +} +#endif + +/* some useful checked functions */ + +/* check that (p ... p + size - 1) lies inside 'p' region, if any */ +static void __bound_check(const void *p, size_t size, const char *function) +{ + if (size != 0 && __bound_ptr_add((void *)p, size) == INVALID_POINTER) { + bound_error("invalid pointer %p, size 0x%lx in %s", + p, (unsigned long)size, function); + } +} + +static int check_overlap (const void *p1, size_t n1, + const void *p2, size_t n2, + const char *function) +{ + const void *p1e = (const void *) ((const char *) p1 + n1); + const void *p2e = (const void *) ((const char *) p2 + n2); + + if (NO_CHECKING_GET() == 0 && n1 != 0 && n2 !=0 && + ((p1 <= p2 && p1e > p2) || /* p1----p2====p1e----p2e */ + (p2 <= p1 && p2e > p1))) { /* p2----p1====p2e----p1e */ + bound_error("overlapping regions %p(0x%lx), %p(0x%lx) in %s", + p1, (unsigned long)n1, p2, (unsigned long)n2, function); + return never_fatal < 0; + } + return 0; +} + +void *__bound_memcpy(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_mempcy_count); + __bound_check(dest, n, "memcpy dest"); + __bound_check(src, n, "memcpy src"); + if (check_overlap(dest, n, src, n, "memcpy")) + return dest; + return memcpy(dest, src, n); +} + +int __bound_memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *u1 = (const unsigned char *) s1; + const unsigned char *u2 = (const unsigned char *) s2; + int retval = 0; + + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, s1, s2, (unsigned long)n); + INCR_COUNT(bound_memcmp_count); + for (;;) { + if ((ssize_t) --n == -1) + break; + else if (*u1 != *u2) { + retval = *u1++ - *u2++; + break; + } + ++u1; + ++u2; + } + __bound_check(s1, (const void *)u1 - s1, "memcmp s1"); + __bound_check(s2, (const void *)u2 - s2, "memcmp s2"); + return retval; +} + +void *__bound_memmove(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return memmove(dest, src, n); +} + +void *__bound_memset(void *s, int c, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %d, 0x%lx\n", + __FILE__, __FUNCTION__, s, c, (unsigned long)n); + INCR_COUNT(bound_memset_count); + __bound_check(s, n, "memset"); + return memset(s, c, n); +} + +#if defined(__arm__) && defined(__ARM_EABI__) +void *__bound___aeabi_memcpy(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_mempcy_count); + __bound_check(dest, n, "memcpy dest"); + __bound_check(src, n, "memcpy src"); + if (check_overlap(dest, n, src, n, "memcpy")) + return dest; + return __aeabi_memcpy(dest, src, n); +} + +void *__bound___aeabi_memmove(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return __aeabi_memmove(dest, src, n); +} + +void *__bound___aeabi_memmove4(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return __aeabi_memmove4(dest, src, n); +} + +void *__bound___aeabi_memmove8(void *dest, const void *src, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_memmove_count); + __bound_check(dest, n, "memmove dest"); + __bound_check(src, n, "memmove src"); + return __aeabi_memmove8(dest, src, n); +} + +void *__bound___aeabi_memset(void *s, int c, size_t n) +{ + dprintf(stderr, "%s, %s(): %p, %d, 0x%lx\n", + __FILE__, __FUNCTION__, s, c, (unsigned long)n); + INCR_COUNT(bound_memset_count); + __bound_check(s, n, "memset"); + return __aeabi_memset(s, c, n); +} +#endif + +int __bound_strlen(const char *s) +{ + const char *p = s; + + dprintf(stderr, "%s, %s(): %p\n", + __FILE__, __FUNCTION__, s); + INCR_COUNT(bound_strlen_count); + while (*p++); + __bound_check(s, p - s, "strlen"); + return (p - s) - 1; +} + +char *__bound_strcpy(char *dest, const char *src) +{ + size_t len; + const char *p = src; + + dprintf(stderr, "%s, %s(): %p, %p\n", + __FILE__, __FUNCTION__, dest, src); + INCR_COUNT(bound_strcpy_count); + while (*p++); + len = p - src; + __bound_check(dest, len, "strcpy dest"); + __bound_check(src, len, "strcpy src"); + if (check_overlap(dest, len, src, len, "strcpy")) + return dest; + return strcpy (dest, src); +} + +char *__bound_strncpy(char *dest, const char *src, size_t n) +{ + size_t len = n; + const char *p = src; + + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_strncpy_count); + while (len-- && *p++); + len = p - src; + __bound_check(dest, len, "strncpy dest"); + __bound_check(src, len, "strncpy src"); + if (check_overlap(dest, len, src, len, "strncpy")) + return dest; + return strncpy(dest, src, n); +} + +int __bound_strcmp(const char *s1, const char *s2) +{ + const unsigned char *u1 = (const unsigned char *) s1; + const unsigned char *u2 = (const unsigned char *) s2; + + dprintf(stderr, "%s, %s(): %p, %p\n", + __FILE__, __FUNCTION__, s1, s2); + INCR_COUNT(bound_strcmp_count); + while (*u1 && *u1 == *u2) { + ++u1; + ++u2; + } + __bound_check(s1, ((const char *)u1 - s1) + 1, "strcmp s1"); + __bound_check(s2, ((const char *)u2 - s2) + 1, "strcmp s2"); + return *u1 - *u2; +} + +int __bound_strncmp(const char *s1, const char *s2, size_t n) +{ + const unsigned char *u1 = (const unsigned char *) s1; + const unsigned char *u2 = (const unsigned char *) s2; + int retval = 0; + + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, s1, s2, (unsigned long)n); + INCR_COUNT(bound_strncmp_count); + do { + if ((ssize_t) --n == -1) + break; + else if (*u1 != *u2) { + retval = *u1++ - *u2++; + break; + } + ++u2; + } while (*u1++); + __bound_check(s1, (const char *)u1 - s1, "strncmp s1"); + __bound_check(s2, (const char *)u2 - s2, "strncmp s2"); + return retval; +} + +char *__bound_strcat(char *dest, const char *src) +{ + char *r = dest; + const char *s = src; + + dprintf(stderr, "%s, %s(): %p, %p\n", + __FILE__, __FUNCTION__, dest, src); + INCR_COUNT(bound_strcat_count); + while (*dest++); + while (*src++); + __bound_check(r, (dest - r) + (src - s) - 1, "strcat dest"); + __bound_check(s, src - s, "strcat src"); + if (check_overlap(r, (dest - r) + (src - s) - 1, s, src - s, "strcat")) + return dest; + return strcat(r, s); +} + +char *__bound_strncat(char *dest, const char *src, size_t n) +{ + char *r = dest; + const char *s = src; + size_t len = n; + + dprintf(stderr, "%s, %s(): %p, %p, 0x%lx\n", + __FILE__, __FUNCTION__, dest, src, (unsigned long)n); + INCR_COUNT(bound_strncat_count); + while (*dest++); + while (len-- && *src++); + __bound_check(r, (dest - r) + (src - s) - 1, "strncat dest"); + __bound_check(s, src - s, "strncat src"); + if (check_overlap(r, (dest - r) + (src - s) - 1, s, src - s, "strncat")) + return dest; + return strncat(r, s, n); +} + +char *__bound_strchr(const char *s, int c) +{ + const unsigned char *str = (const unsigned char *) s; + unsigned char ch = c; + + dprintf(stderr, "%s, %s(): %p, %d\n", + __FILE__, __FUNCTION__, s, ch); + INCR_COUNT(bound_strchr_count); + while (*str) { + if (*str == ch) + break; + ++str; + } + __bound_check(s, ((const char *)str - s) + 1, "strchr"); + return *str == ch ? (char *) str : NULL; +} + +char *__bound_strrchr(const char *s, int c) +{ + const unsigned char *str = (const unsigned char *) s; + unsigned char ch = c; + + dprintf(stderr, "%s, %s(): %p, %d\n", + __FILE__, __FUNCTION__, s, ch); + INCR_COUNT(bound_strrchr_count); + while (*str++); + __bound_check(s, (const char *)str - s, "strrchr"); + while (str != (const unsigned char *)s) { + if (*--str == ch) + break; + } + __bound_check(s, (const char *)str - s, "strrchr"); + return *str == ch ? (char *) str : NULL; +} + +char *__bound_strdup(const char *s) +{ + const char *p = s; + char *new; + + INCR_COUNT(bound_strdup_count); + while (*p++); + __bound_check(s, p - s, "strdup"); + new = BOUND_MALLOC ((p - s) + 1); + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, new, (unsigned long)(p -s)); + if (new) { + if (NO_CHECKING_GET() == 0 && no_strdup == 0) { + WAIT_SEM (); + tree = splay_insert((size_t)new, p - s, tree); + if (tree && tree->start == (size_t) new) + tree->type = TCC_TYPE_STRDUP; + POST_SEM (); + } + memcpy (new, s, p - s); + } + return new; +} + +/* + An implementation of top-down splaying with sizes + D. Sleator <sleator@cs.cmu.edu>, January 1994. + + This extends top-down-splay.c to maintain a size field in each node. + This is the number of nodes in the subtree rooted there. This makes + it possible to efficiently compute the rank of a key. (The rank is + the number of nodes to the left of the given key.) It it also + possible to quickly find the node of a given rank. Both of these + operations are illustrated in the code below. The remainder of this + introduction is taken from top-down-splay.c. + + "Splay trees", or "self-adjusting search trees" are a simple and + efficient data structure for storing an ordered set. The data + structure consists of a binary tree, with no additional fields. It + allows searching, insertion, deletion, deletemin, deletemax, + splitting, joining, and many other operations, all with amortized + logarithmic performance. Since the trees adapt to the sequence of + requests, their performance on real access patterns is typically even + better. Splay trees are described in a number of texts and papers + [1,2,3,4]. + + The code here is adapted from simple top-down splay, at the bottom of + page 669 of [2]. It can be obtained via anonymous ftp from + spade.pc.cs.cmu.edu in directory /usr/sleator/public. + + The chief modification here is that the splay operation works even if the + item being splayed is not in the tree, and even if the tree root of the + tree is NULL. So the line: + + t = splay(i, t); + + causes it to search for item with key i in the tree rooted at t. If it's + there, it is splayed to the root. If it isn't there, then the node put + at the root is the last one before NULL that would have been reached in a + normal binary search for i. (It's a neighbor of i in the tree.) This + allows many other operations to be easily implemented, as shown below. + + [1] "Data Structures and Their Algorithms", Lewis and Denenberg, + Harper Collins, 1991, pp 243-251. + [2] "Self-adjusting Binary Search Trees" Sleator and Tarjan, + JACM Volume 32, No 3, July 1985, pp 652-686. + [3] "Data Structure and Algorithm Analysis", Mark Weiss, + Benjamin Cummins, 1992, pp 119-130. + [4] "Data Structures, Algorithms, and Performance", Derick Wood, + Addison-Wesley, 1993, pp 367-375 +*/ + +/* Code adapted for tcc */ + +#define compare(start,tstart,tsize) (start < tstart ? -1 : \ + start >= tstart+tsize ? 1 : 0) + +static Tree * splay (size_t addr, Tree *t) +/* Splay using the key start (which may or may not be in the tree.) */ +/* The starting root is t, and the tree used is defined by rat */ +{ + Tree N, *l, *r, *y; + int comp; + + INCR_COUNT_SPLAY(bound_splay); + if (t == NULL) return t; + N.left = N.right = NULL; + l = r = &N; + + for (;;) { + comp = compare(addr, t->start, t->size); + if (comp < 0) { + y = t->left; + if (y == NULL) break; + if (compare(addr, y->start, y->size) < 0) { + t->left = y->right; /* rotate right */ + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } else if (comp > 0) { + y = t->right; + if (y == NULL) break; + if (compare(addr, y->start, y->size) > 0) { + t->right = y->left; /* rotate left */ + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } else { + break; + } + } + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + + return t; +} + +#define compare_end(start,tend) (start < tend ? -1 : \ + start > tend ? 1 : 0) + +static Tree * splay_end (size_t addr, Tree *t) +/* Splay using the key start (which may or may not be in the tree.) */ +/* The starting root is t, and the tree used is defined by rat */ +{ + Tree N, *l, *r, *y; + int comp; + + INCR_COUNT_SPLAY(bound_splay_end); + if (t == NULL) return t; + N.left = N.right = NULL; + l = r = &N; + + for (;;) { + comp = compare_end(addr, t->start + t->size); + if (comp < 0) { + y = t->left; + if (y == NULL) break; + if (compare_end(addr, y->start + y->size) < 0) { + t->left = y->right; /* rotate right */ + y->right = t; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + } else if (comp > 0) { + y = t->right; + if (y == NULL) break; + if (compare_end(addr, y->start + y->size) > 0) { + t->right = y->left; /* rotate left */ + y->left = t; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + } else { + break; + } + } + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + + return t; +} + +static Tree * splay_insert(size_t addr, size_t size, Tree * t) +/* Insert key start into the tree t, if it is not already there. */ +/* Return a pointer to the resulting tree. */ +{ + Tree * new; + + INCR_COUNT_SPLAY(bound_splay_insert); + if (t != NULL) { + t = splay(addr,t); + if (compare(addr, t->start, t->size)==0) { + return t; /* it's already there */ + } + } +#if TREE_REUSE + if (tree_free_list) { + new = tree_free_list; + tree_free_list = new->left; + } + else +#endif + { + new = (Tree *) BOUND_MALLOC (sizeof (Tree)); + } + if (new == NULL) { + bound_alloc_error("not enough memory for bound checking code"); + } + else { + if (t == NULL) { + new->left = new->right = NULL; + } else if (compare(addr, t->start, t->size) < 0) { + new->left = t->left; + new->right = t; + t->left = NULL; + } else { + new->right = t->right; + new->left = t; + t->right = NULL; + } + new->start = addr; + new->size = size; + new->type = TCC_TYPE_NONE; + new->is_invalid = 0; + } + return new; +} + +#define compare_destroy(start,tstart) (start < tstart ? -1 : \ + start > tstart ? 1 : 0) + +static Tree * splay_delete(size_t addr, Tree *t) +/* Deletes addr from the tree if it's there. */ +/* Return a pointer to the resulting tree. */ +{ + Tree * x; + + INCR_COUNT_SPLAY(bound_splay_delete); + if (t==NULL) return NULL; + t = splay(addr,t); + if (compare_destroy(addr, t->start) == 0) { /* found it */ + if (t->left == NULL) { + x = t->right; + } else { + x = splay(addr, t->left); + x->right = t->right; + } +#if TREE_REUSE + t->left = tree_free_list; + tree_free_list = t; +#else + BOUND_FREE(t); +#endif + return x; + } else { + return t; /* It wasn't there */ + } +} + +void splay_printtree(Tree * t, int d) +{ + int i; + if (t == NULL) return; + splay_printtree(t->right, d+1); + for (i=0; i<d; i++) fprintf(stderr," "); + fprintf(stderr,"%p(0x%lx:%u:%u)\n", + (void *) t->start, (unsigned long) t->size, + (unsigned)t->type, (unsigned)t->is_invalid); + splay_printtree(t->left, d+1); +} diff --git a/tinycc/lib/bt-dll.c b/tinycc/lib/bt-dll.c new file mode 100644 index 0000000..7c62cef --- /dev/null +++ b/tinycc/lib/bt-dll.c @@ -0,0 +1,74 @@ +/* ------------------------------------------------------------- */ +/* stubs for calling bcheck functions from a dll. */ + +#include <windows.h> +#include <stdio.h> + +#define REDIR_ALL \ + REDIR(__bt_init) \ + REDIR(__bt_exit) \ + REDIR(tcc_backtrace) \ + \ + REDIR(__bound_ptr_add) \ + REDIR(__bound_ptr_indir1) \ + REDIR(__bound_ptr_indir2) \ + REDIR(__bound_ptr_indir4) \ + REDIR(__bound_ptr_indir8) \ + REDIR(__bound_ptr_indir12) \ + REDIR(__bound_ptr_indir16) \ + REDIR(__bound_local_new) \ + REDIR(__bound_local_delete) \ + REDIR(__bound_new_region) \ + \ + REDIR(__bound_free) \ + REDIR(__bound_malloc) \ + REDIR(__bound_realloc) \ + REDIR(__bound_memcpy) \ + REDIR(__bound_memcmp) \ + REDIR(__bound_memmove) \ + REDIR(__bound_memset) \ + REDIR(__bound_strlen) \ + REDIR(__bound_strcpy) \ + REDIR(__bound_strncpy) \ + REDIR(__bound_strcmp) \ + REDIR(__bound_strncmp) \ + REDIR(__bound_strcat) \ + REDIR(__bound_strchr) \ + REDIR(__bound_strdup) + +#ifdef __leading_underscore +#define _(s) "_"#s +#else +#define _(s) #s +#endif + +#define REDIR(s) void *s; +static struct { REDIR_ALL } all_ptrs; +#undef REDIR +#define REDIR(s) #s"\0" +static const char all_names[] = REDIR_ALL; +#undef REDIR +#define REDIR(s) __asm__(".global " _(s) ";" _(s) ": jmp *%0" : : "m" (all_ptrs.s) ); +static void all_jmps() { REDIR_ALL } +#undef REDIR + +void __bt_init_dll(int bcheck) +{ + const char *s = all_names; + void **p = (void**)&all_ptrs; + do { + *p = (void*)GetProcAddress(GetModuleHandle(NULL), (char*)s); + if (NULL == *p) { + char buf[100]; + sprintf(buf, + "Error: function '%s()' not found in executable. " + "(Need -bt or -b for linking the exe.)", s); + if (GetStdHandle(STD_ERROR_HANDLE)) + fprintf(stderr, "TCC/BCHECK: %s\n", buf), fflush(stderr); + else + MessageBox(NULL, buf, "TCC/BCHECK", MB_ICONERROR); + ExitProcess(1); + } + s = strchr(s,'\0') + 1, ++p; + } while (*s && (bcheck || p < &all_ptrs.__bound_ptr_add)); +} diff --git a/tinycc/lib/bt-exe.c b/tinycc/lib/bt-exe.c new file mode 100644 index 0000000..3a2d02e --- /dev/null +++ b/tinycc/lib/bt-exe.c @@ -0,0 +1,74 @@ +/* ------------------------------------------------------------- */ +/* for linking rt_printline and the signal/exception handler + from tccrun.c into executables. */ + +#define CONFIG_TCC_BACKTRACE_ONLY +#define ONE_SOURCE 1 +#define pstrcpy tcc_pstrcpy +#include "../tccrun.c" + +int (*__rt_error)(void*, void*, const char *, va_list); +__attribute__((weak)) void __bound_checking_lock(void); +__attribute__((weak)) void __bound_checking_unlock(void); + +#ifndef _WIN32 +# define __declspec(n) +#endif + +__declspec(dllexport) +void __bt_init(rt_context *p, int num_callers) +{ + __attribute__((weak)) int main(); + __attribute__((weak)) void __bound_init(void*, int); + struct rt_context *rc = &g_rtctxt; + //fprintf(stderr, "__bt_init %d %p %p\n", num_callers, p->stab_sym, p->bounds_start), fflush(stderr); + /* call __bound_init here due to redirection of sigaction */ + /* needed to add global symbols */ + if (p->bounds_start) { + __bound_init(p->bounds_start, -1); + __bound_checking_lock(); + } + if (num_callers) { + memcpy(rc, p, offsetof(rt_context, next)); + rc->num_callers = num_callers - 1; + rc->top_func = main; + __rt_error = _rt_error; + set_exception_handler(); + } else { + p->next = rc->next, rc->next = p; + } + if (p->bounds_start) + __bound_checking_unlock(); +} + +__declspec(dllexport) +void __bt_exit(rt_context *p) +{ + __attribute__((weak)) void __bound_exit_dll(void*); + struct rt_context *rc = &g_rtctxt; + + if (p->bounds_start) { + __bound_exit_dll(p->bounds_start); + __bound_checking_lock(); + } + while (rc) { + if (rc->next == p) { + rc->next = rc->next->next; + break; + } + rc = rc->next; + } + if (p->bounds_start) + __bound_checking_unlock(); +} + +/* copy a string and truncate it. */ +ST_FUNC char *pstrcpy(char *buf, size_t buf_size, const char *s) +{ + int l = strlen(s); + if (l >= buf_size) + l = buf_size - 1; + memcpy(buf, s, l); + buf[l] = 0; + return buf; +} diff --git a/tinycc/lib/bt-log.c b/tinycc/lib/bt-log.c new file mode 100644 index 0000000..8f7a4db --- /dev/null +++ b/tinycc/lib/bt-log.c @@ -0,0 +1,47 @@ +/* ------------------------------------------------------------- */ +/* function to get a stack backtrace on demand with a message */ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +int (*__rt_error)(void*, void*, const char *, va_list); + +#ifdef _WIN32 +# define DLL_EXPORT __declspec(dllexport) +#else +# define DLL_EXPORT +#endif + +/* Needed when using ...libtcc1-usegcc=yes in lib/Makefile */ +#if (defined(__GNUC__) && (__GNUC__ >= 6)) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wframe-address" +#endif + +DLL_EXPORT int tcc_backtrace(const char *fmt, ...) +{ + va_list ap; + int ret; + + if (__rt_error) { + void *fp = __builtin_frame_address(1); + void *ip = __builtin_return_address(0); + va_start(ap, fmt); + ret = __rt_error(fp, ip, fmt, ap); + va_end(ap); + } else { + const char *p; + if (fmt[0] == '^' && (p = strchr(fmt + 1, fmt[0]))) + fmt = p + 1; + va_start(ap, fmt); + ret = vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"), fflush(stderr); + } + return ret; +} + +#if (defined(__GNUC__) && (__GNUC__ >= 6)) || defined(__clang__) +#pragma GCC diagnostic pop +#endif diff --git a/tinycc/lib/builtin.c b/tinycc/lib/builtin.c new file mode 100644 index 0000000..e40a003 --- /dev/null +++ b/tinycc/lib/builtin.c @@ -0,0 +1,164 @@ +/* uses alias to allow building with gcc/clang */ +#ifdef __TINYC__ +#define BUILTIN(x) __builtin_##x +#define BUILTINN(x) "__builtin_" # x +#else +#define BUILTIN(x) __tcc_builtin_##x +#define BUILTINN(x) "__tcc_builtin_" # x +#endif + +/* ---------------------------------------------- */ +/* This file implements: + * __builtin_ffs + * __builtin_clz + * __builtin_ctz + * __builtin_clrsb + * __builtin_popcount + * __builtin_parity + * for int, long and long long + */ + +static const unsigned char table_1_32[] = { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 +}; +static const unsigned char table_2_32[32] = { + 31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1, + 23, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0 +}; +static const unsigned char table_1_64[] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 +}; +static const unsigned char table_2_64[] = { + 63, 16, 62, 7, 15, 36, 61, 3, 6, 14, 22, 26, 35, 47, 60, 2, + 9, 5, 28, 11, 13, 21, 42, 19, 25, 31, 34, 40, 46, 52, 59, 1, + 17, 8, 37, 4, 23, 27, 48, 10, 29, 12, 43, 20, 32, 41, 53, 18, + 38, 24, 49, 30, 44, 33, 54, 39, 50, 45, 55, 51, 56, 57, 58, 0 +}; + +#define FFSI(x) \ + return table_1_32[((x & -x) * 0x077cb531u) >> 27] + (x != 0); +#define FFSL(x) \ + return table_1_64[((x & -x) * 0x022fdd63cc95386dull) >> 58] + (x != 0); +#define CTZI(x) \ + return table_1_32[((x & -x) * 0x077cb531u) >> 27]; +#define CTZL(x) \ + return table_1_64[((x & -x) * 0x022fdd63cc95386dull) >> 58]; +#define CLZI(x) \ + x |= x >> 1; \ + x |= x >> 2; \ + x |= x >> 4; \ + x |= x >> 8; \ + x |= x >> 16; \ + return table_2_32[(x * 0x07c4acddu) >> 27]; +#define CLZL(x) \ + x |= x >> 1; \ + x |= x >> 2; \ + x |= x >> 4; \ + x |= x >> 8; \ + x |= x >> 16; \ + x |= x >> 32; \ + return table_2_64[x * 0x03f79d71b4cb0a89ull >> 58]; +#define POPCOUNTI(x, m) \ + x = x - ((x >> 1) & 0x55555555); \ + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); \ + x = (x + (x >> 4)) & 0xf0f0f0f; \ + return ((x * 0x01010101) >> 24) & m; +#define POPCOUNTL(x, m) \ + x = x - ((x >> 1) & 0x5555555555555555ull); \ + x = (x & 0x3333333333333333ull) + ((x >> 2) & 0x3333333333333333ull); \ + x = (x + (x >> 4)) & 0xf0f0f0f0f0f0f0full; \ + return ((x * 0x0101010101010101ull) >> 56) & m; + +/* Returns one plus the index of the least significant 1-bit of x, + or if x is zero, returns zero. */ +int BUILTIN(ffs) (int x) { FFSI(x) } +int BUILTIN(ffsll) (long long x) { FFSL(x) } +#if __SIZEOF_LONG__ == 4 +int BUILTIN(ffsl) (long x) __attribute__((alias(BUILTINN(ffs)))); +#else +int BUILTIN(ffsl) (long x) __attribute__((alias(BUILTINN(ffsll)))); +#endif + +/* Returns the number of leading 0-bits in x, starting at the most significant + bit position. If x is 0, the result is undefined. */ +int BUILTIN(clz) (unsigned int x) { CLZI(x) } +int BUILTIN(clzll) (unsigned long long x) { CLZL(x) } +#if __SIZEOF_LONG__ == 4 +int BUILTIN(clzl) (unsigned long x) __attribute__((alias(BUILTINN(clz)))); +#else +int BUILTIN(clzl) (unsigned long x) __attribute__((alias(BUILTINN(clzll)))); +#endif + +/* Returns the number of trailing 0-bits in x, starting at the least + significant bit position. If x is 0, the result is undefined. */ +int BUILTIN(ctz) (unsigned int x) { CTZI(x) } +int BUILTIN(ctzll) (unsigned long long x) { CTZL(x) } +#if __SIZEOF_LONG__ == 4 +int BUILTIN(ctzl) (unsigned long x) __attribute__((alias(BUILTINN(ctz)))); +#else +int BUILTIN(ctzl) (unsigned long x) __attribute__((alias(BUILTINN(ctzll)))); +#endif + +/* Returns the number of leading redundant sign bits in x, i.e. the number + of bits following the most significant bit that are identical to it. + There are no special cases for 0 or other values. */ +int BUILTIN(clrsb) (int x) { if (x < 0) x = ~x; x <<= 1; CLZI(x) } +int BUILTIN(clrsbll) (long long x) { if (x < 0) x = ~x; x <<= 1; CLZL(x) } +#if __SIZEOF_LONG__ == 4 +int BUILTIN(clrsbl) (long x) __attribute__((alias(BUILTINN(clrsb)))); +#else +int BUILTIN(clrsbl) (long x) __attribute__((alias(BUILTINN(clrsbll)))); +#endif + +/* Returns the number of 1-bits in x.*/ +int BUILTIN(popcount) (unsigned int x) { POPCOUNTI(x, 0x3f) } +int BUILTIN(popcountll) (unsigned long long x) { POPCOUNTL(x, 0x7f) } +#if __SIZEOF_LONG__ == 4 +int BUILTIN(popcountl) (unsigned long x) __attribute__((alias(BUILTINN(popcount)))); +#else +int BUILTIN(popcountl ) (unsigned long x) __attribute__((alias(BUILTINN(popcountll)))); +#endif + +/* Returns the parity of x, i.e. the number of 1-bits in x modulo 2. */ +int BUILTIN(parity) (unsigned int x) { POPCOUNTI(x, 0x01) } +int BUILTIN(parityll) (unsigned long long x) { POPCOUNTL(x, 0x01) } +#if __SIZEOF_LONG__ == 4 +int BUILTIN(parityl) (unsigned long x) __attribute__((alias(BUILTINN(parity)))); +#else +int BUILTIN(parityl) (unsigned long x) __attribute__((alias(BUILTINN(parityll)))); +#endif + +#ifndef __TINYC__ +#if defined(__GNUC__) && (__GNUC__ >= 6) +/* gcc overrides alias from __builtin_ffs... to ffs.. so use assembly code */ +__asm__(".globl __builtin_ffs"); +__asm__(".set __builtin_ffs,__tcc_builtin_ffs"); +__asm__(".globl __builtin_ffsl"); +__asm__(".set __builtin_ffsl,__tcc_builtin_ffsl"); +__asm__(".globl __builtin_ffsll"); +__asm__(".set __builtin_ffsll,__tcc_builtin_ffsll"); +#else +int __builtin_ffs(int x) __attribute__((alias("__tcc_builtin_ffs"))); +int __builtin_ffsl(long x) __attribute__((alias("__tcc_builtin_ffsl"))); +int __builtin_ffsll(long long x) __attribute__((alias("__tcc_builtin_ffsll"))); +#endif +int __builtin_clz(unsigned int x) __attribute__((alias("__tcc_builtin_clz"))); +int __builtin_clzl(unsigned long x) __attribute__((alias("__tcc_builtin_clzl"))); +int __builtin_clzll(unsigned long long x) __attribute__((alias("__tcc_builtin_clzll"))); +int __builtin_ctz(unsigned int x) __attribute__((alias("__tcc_builtin_ctz"))); +int __builtin_ctzl(unsigned long x) __attribute__((alias("__tcc_builtin_ctzl"))); +int __builtin_ctzll(unsigned long long x) __attribute__((alias("__tcc_builtin_ctzll"))); +int __builtin_clrsb(int x) __attribute__((alias("__tcc_builtin_clrsb"))); +int __builtin_clrsbl(long x) __attribute__((alias("__tcc_builtin_clrsbl"))); +int __builtin_clrsbll(long long x) __attribute__((alias("__tcc_builtin_clrsbll"))); +int __builtin_popcount(unsigned int x) __attribute__((alias("__tcc_builtin_popcount"))); +int __builtin_popcountl(unsigned long x) __attribute__((alias("__tcc_builtin_popcountl"))); +int __builtin_popcountll(unsigned long long x) __attribute__((alias("__tcc_builtin_popcountll"))); +int __builtin_parity(unsigned int x) __attribute__((alias("__tcc_builtin_parity"))); +int __builtin_parityl(unsigned long x) __attribute__((alias("__tcc_builtin_parityl"))); +int __builtin_parityll(unsigned long long x) __attribute__((alias("__tcc_builtin_parityll"))); +#endif diff --git a/tinycc/lib/dsohandle.c b/tinycc/lib/dsohandle.c new file mode 100644 index 0000000..0993dbc --- /dev/null +++ b/tinycc/lib/dsohandle.c @@ -0,0 +1 @@ +void * __dso_handle __attribute((visibility("hidden"))) = &__dso_handle; diff --git a/tinycc/lib/lib-arm64.c b/tinycc/lib/lib-arm64.c new file mode 100644 index 0000000..226827e --- /dev/null +++ b/tinycc/lib/lib-arm64.c @@ -0,0 +1,677 @@ +/* + * TCC runtime library for arm64. + * + * Copyright (c) 2015 Edmund Grimley Evans + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. This file is offered as-is, + * without any warranty. + */ + +#ifdef __TINYC__ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +void *memcpy(void*,void*,__SIZE_TYPE__); +#else +#include <stdint.h> +#include <string.h> +#endif + +#if !defined __riscv && !defined __APPLE__ +void __clear_cache(void *beg, void *end) +{ + __arm64_clear_cache(beg, end); +} +#endif + +typedef struct { + uint64_t x0, x1; +} u128_t; + +static long double f3_zero(int sgn) +{ + long double f; + u128_t x = { 0, (uint64_t)sgn << 63 }; + memcpy(&f, &x, 16); + return f; +} + +static long double f3_infinity(int sgn) +{ + long double f; + u128_t x = { 0, (uint64_t)sgn << 63 | 0x7fff000000000000 }; + memcpy(&f, &x, 16); + return f; +} + +static long double f3_NaN(void) +{ + long double f; +#if 0 + // ARM's default NaN usually has just the top fraction bit set: + u128_t x = { 0, 0x7fff800000000000 }; +#else + // GCC's library sets all fraction bits: + u128_t x = { -1, 0x7fffffffffffffff }; +#endif + memcpy(&f, &x, 16); + return f; +} + +static int fp3_convert_NaN(long double *f, int sgn, u128_t mnt) +{ + u128_t x = { mnt.x0, + mnt.x1 | 0x7fff800000000000 | (uint64_t)sgn << 63 }; + memcpy(f, &x, 16); + return 1; +} + +static int fp3_detect_NaNs(long double *f, + int a_sgn, int a_exp, u128_t a, + int b_sgn, int b_exp, u128_t b) +{ + // Detect signalling NaNs: + if (a_exp == 32767 && (a.x0 | a.x1 << 16) && !(a.x1 >> 47 & 1)) + return fp3_convert_NaN(f, a_sgn, a); + if (b_exp == 32767 && (b.x0 | b.x1 << 16) && !(b.x1 >> 47 & 1)) + return fp3_convert_NaN(f, b_sgn, b); + + // Detect quiet NaNs: + if (a_exp == 32767 && (a.x0 | a.x1 << 16)) + return fp3_convert_NaN(f, a_sgn, a); + if (b_exp == 32767 && (b.x0 | b.x1 << 16)) + return fp3_convert_NaN(f, b_sgn, b); + + return 0; +} + +static void f3_unpack(int *sgn, int32_t *exp, u128_t *mnt, long double f) +{ + u128_t x; + memcpy(&x, &f, 16); + *sgn = x.x1 >> 63; + *exp = x.x1 >> 48 & 32767; + x.x1 = x.x1 << 16 >> 16; + if (*exp) + x.x1 |= (uint64_t)1 << 48; + else + *exp = 1; + *mnt = x; +} + +static u128_t f3_normalise(int32_t *exp, u128_t mnt) +{ + int sh; + if (!(mnt.x0 | mnt.x1)) + return mnt; + if (!mnt.x1) { + mnt.x1 = mnt.x0; + mnt.x0 = 0; + *exp -= 64; + } + for (sh = 32; sh; sh >>= 1) { + if (!(mnt.x1 >> (64 - sh))) { + mnt.x1 = mnt.x1 << sh | mnt.x0 >> (64 - sh); + mnt.x0 = mnt.x0 << sh; + *exp -= sh; + } + } + return mnt; +} + +static u128_t f3_sticky_shift(int32_t sh, u128_t x) +{ + if (sh >= 128) { + x.x0 = !!(x.x0 | x.x1); + x.x1 = 0; + return x; + } + if (sh >= 64) { + x.x0 = x.x1 | !!x.x0; + x.x1 = 0; + sh -= 64; + } + if (sh > 0) { + x.x0 = x.x0 >> sh | x.x1 << (64 - sh) | !!(x.x0 << (64 - sh)); + x.x1 = x.x1 >> sh; + } + return x; +} + +static long double f3_round(int sgn, int32_t exp, u128_t x) +{ + long double f; + int error; + + if (exp > 0) { + x = f3_sticky_shift(13, x); + } + else { + x = f3_sticky_shift(14 - exp, x); + exp = 0; + } + + error = x.x0 & 3; + x.x0 = x.x0 >> 2 | x.x1 << 62; + x.x1 = x.x1 >> 2; + + if (error == 3 || ((error == 2) & (x.x0 & 1))) { + if (!++x.x0) { + ++x.x1; + if (x.x1 == (uint64_t)1 << 48) + exp = 1; + else if (x.x1 == (uint64_t)1 << 49) { + ++exp; + x.x0 = x.x0 >> 1 | x.x1 << 63; + x.x1 = x.x1 >> 1; + } + } + } + + if (exp >= 32767) + return f3_infinity(sgn); + + x.x1 = x.x1 << 16 >> 16 | (uint64_t)exp << 48 | (uint64_t)sgn << 63; + memcpy(&f, &x, 16); + return f; +} + +static long double f3_add(long double fa, long double fb, int neg) +{ + u128_t a, b, x; + int32_t a_exp, b_exp, x_exp; + int a_sgn, b_sgn, x_sgn; + long double fx; + + f3_unpack(&a_sgn, &a_exp, &a, fa); + f3_unpack(&b_sgn, &b_exp, &b, fb); + + if (fp3_detect_NaNs(&fx, a_sgn, a_exp, a, b_sgn, b_exp, b)) + return fx; + + b_sgn ^= neg; + + // Handle infinities and zeroes: + if (a_exp == 32767 && b_exp == 32767 && a_sgn != b_sgn) + return f3_NaN(); + if (a_exp == 32767) + return f3_infinity(a_sgn); + if (b_exp == 32767) + return f3_infinity(b_sgn); + if (!(a.x0 | a.x1 | b.x0 | b.x1)) + return f3_zero(a_sgn & b_sgn); + + a.x1 = a.x1 << 3 | a.x0 >> 61; + a.x0 = a.x0 << 3; + b.x1 = b.x1 << 3 | b.x0 >> 61; + b.x0 = b.x0 << 3; + + if (a_exp <= b_exp) { + a = f3_sticky_shift(b_exp - a_exp, a); + a_exp = b_exp; + } + else { + b = f3_sticky_shift(a_exp - b_exp, b); + b_exp = a_exp; + } + + x_sgn = a_sgn; + x_exp = a_exp; + if (a_sgn == b_sgn) { + x.x0 = a.x0 + b.x0; + x.x1 = a.x1 + b.x1 + (x.x0 < a.x0); + } + else { + x.x0 = a.x0 - b.x0; + x.x1 = a.x1 - b.x1 - (x.x0 > a.x0); + if (x.x1 >> 63) { + x_sgn ^= 1; + x.x0 = -x.x0; + x.x1 = -x.x1 - !!x.x0; + } + } + + if (!(x.x0 | x.x1)) + return f3_zero(0); + + x = f3_normalise(&x_exp, x); + + return f3_round(x_sgn, x_exp + 12, x); +} + +long double __addtf3(long double a, long double b) +{ + return f3_add(a, b, 0); +} + +long double __subtf3(long double a, long double b) +{ + return f3_add(a, b, 1); +} + +long double __multf3(long double fa, long double fb) +{ + u128_t a, b, x; + int32_t a_exp, b_exp, x_exp; + int a_sgn, b_sgn, x_sgn; + long double fx; + + f3_unpack(&a_sgn, &a_exp, &a, fa); + f3_unpack(&b_sgn, &b_exp, &b, fb); + + if (fp3_detect_NaNs(&fx, a_sgn, a_exp, a, b_sgn, b_exp, b)) + return fx; + + // Handle infinities and zeroes: + if ((a_exp == 32767 && !(b.x0 | b.x1)) || + (b_exp == 32767 && !(a.x0 | a.x1))) + return f3_NaN(); + if (a_exp == 32767 || b_exp == 32767) + return f3_infinity(a_sgn ^ b_sgn); + if (!(a.x0 | a.x1) || !(b.x0 | b.x1)) + return f3_zero(a_sgn ^ b_sgn); + + a = f3_normalise(&a_exp, a); + b = f3_normalise(&b_exp, b); + + x_sgn = a_sgn ^ b_sgn; + x_exp = a_exp + b_exp - 16352; + + { + // Convert to base (1 << 30), discarding bottom 6 bits, which are zero, + // so there are (32, 30, 30, 30) bits in (a3, a2, a1, a0): + uint64_t a0 = a.x0 << 28 >> 34; + uint64_t b0 = b.x0 << 28 >> 34; + uint64_t a1 = a.x0 >> 36 | a.x1 << 62 >> 34; + uint64_t b1 = b.x0 >> 36 | b.x1 << 62 >> 34; + uint64_t a2 = a.x1 << 32 >> 34; + uint64_t b2 = b.x1 << 32 >> 34; + uint64_t a3 = a.x1 >> 32; + uint64_t b3 = b.x1 >> 32; + // Use 16 small multiplications and additions that do not overflow: + uint64_t x0 = a0 * b0; + uint64_t x1 = (x0 >> 30) + a0 * b1 + a1 * b0; + uint64_t x2 = (x1 >> 30) + a0 * b2 + a1 * b1 + a2 * b0; + uint64_t x3 = (x2 >> 30) + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + uint64_t x4 = (x3 >> 30) + a1 * b3 + a2 * b2 + a3 * b1; + uint64_t x5 = (x4 >> 30) + a2 * b3 + a3 * b2; + uint64_t x6 = (x5 >> 30) + a3 * b3; + // We now have (64, 30, 30, ...) bits in (x6, x5, x4, ...). + // Take the top 128 bits, setting bottom bit if any lower bits were set: + uint64_t y0 = (x5 << 34 | x4 << 34 >> 30 | x3 << 34 >> 60 | + !!(x3 << 38 | (x2 | x1 | x0) << 34)); + uint64_t y1 = x6; + // Top bit may be zero. Renormalise: + if (!(y1 >> 63)) { + y1 = y1 << 1 | y0 >> 63; + y0 = y0 << 1; + --x_exp; + } + x.x0 = y0; + x.x1 = y1; + } + + return f3_round(x_sgn, x_exp, x); +} + +long double __divtf3(long double fa, long double fb) +{ + u128_t a, b, x; + int32_t a_exp, b_exp, x_exp; + int a_sgn, b_sgn, x_sgn, i; + long double fx; + + f3_unpack(&a_sgn, &a_exp, &a, fa); + f3_unpack(&b_sgn, &b_exp, &b, fb); + + if (fp3_detect_NaNs(&fx, a_sgn, a_exp, a, b_sgn, b_exp, b)) + return fx; + + // Handle infinities and zeroes: + if ((a_exp == 32767 && b_exp == 32767) || + (!(a.x0 | a.x1) && !(b.x0 | b.x1))) + return f3_NaN(); + if (a_exp == 32767 || !(b.x0 | b.x1)) + return f3_infinity(a_sgn ^ b_sgn); + if (!(a.x0 | a.x1) || b_exp == 32767) + return f3_zero(a_sgn ^ b_sgn); + + a = f3_normalise(&a_exp, a); + b = f3_normalise(&b_exp, b); + + x_sgn = a_sgn ^ b_sgn; + x_exp = a_exp - b_exp + 16395; + + a.x0 = a.x0 >> 1 | a.x1 << 63; + a.x1 = a.x1 >> 1; + b.x0 = b.x0 >> 1 | b.x1 << 63; + b.x1 = b.x1 >> 1; + x.x0 = 0; + x.x1 = 0; + for (i = 0; i < 116; i++) { + x.x1 = x.x1 << 1 | x.x0 >> 63; + x.x0 = x.x0 << 1; + if (a.x1 > b.x1 || (a.x1 == b.x1 && a.x0 >= b.x0)) { + a.x1 = a.x1 - b.x1 - (a.x0 < b.x0); + a.x0 = a.x0 - b.x0; + x.x0 |= 1; + } + a.x1 = a.x1 << 1 | a.x0 >> 63; + a.x0 = a.x0 << 1; + } + x.x0 |= !!(a.x0 | a.x1); + + x = f3_normalise(&x_exp, x); + + return f3_round(x_sgn, x_exp, x); +} + +long double __extendsftf2(float f) +{ + long double fx; + u128_t x; + uint32_t a; + uint64_t aa; + memcpy(&a, &f, 4); + aa = a; + x.x0 = 0; + if (!(a << 1)) + x.x1 = aa << 32; + else if (a << 1 >> 24 == 255) + x.x1 = (0x7fff000000000000 | aa >> 31 << 63 | aa << 41 >> 16 | + (uint64_t)!!(a << 9) << 47); + else if (a << 1 >> 24 == 0) { + uint64_t adj = 0; + while (!(a << 1 >> 1 >> (23 - adj))) + adj++; + x.x1 = aa >> 31 << 63 | (16256 - adj + 1) << 48 | aa << adj << 41 >> 16; + } else + x.x1 = (aa >> 31 << 63 | ((aa >> 23 & 255) + 16256) << 48 | + aa << 41 >> 16); + memcpy(&fx, &x, 16); + return fx; +} + +long double __extenddftf2(double f) +{ + long double fx; + u128_t x; + uint64_t a; + memcpy(&a, &f, 8); + x.x0 = a << 60; + if (!(a << 1)) + x.x1 = a; + else if (a << 1 >> 53 == 2047) + x.x1 = (0x7fff000000000000 | a >> 63 << 63 | a << 12 >> 16 | + (uint64_t)!!(a << 12) << 47); + else if (a << 1 >> 53 == 0) { + uint64_t adj = 0; + while (!(a << 1 >> 1 >> (52 - adj))) + adj++; + x.x0 <<= adj; + x.x1 = a >> 63 << 63 | (15360 - adj + 1) << 48 | a << adj << 12 >> 16; + } else + x.x1 = a >> 63 << 63 | ((a >> 52 & 2047) + 15360) << 48 | a << 12 >> 16; + memcpy(&fx, &x, 16); + return fx; +} + +float __trunctfsf2(long double f) +{ + u128_t mnt; + int32_t exp; + int sgn; + uint32_t x; + float fx; + + f3_unpack(&sgn, &exp, &mnt, f); + + if (exp == 32767 && (mnt.x0 | mnt.x1 << 16)) + x = 0x7fc00000 | (uint32_t)sgn << 31 | (mnt.x1 >> 25 & 0x007fffff); + else if (exp > 16510) + x = 0x7f800000 | (uint32_t)sgn << 31; + else if (exp < 16233) + x = (uint32_t)sgn << 31; + else { + exp -= 16257; + x = mnt.x1 >> 23 | !!(mnt.x0 | mnt.x1 << 41); + if (exp < 0) { + x = x >> -exp | !!(x << (32 + exp)); + exp = 0; + } + if ((x & 3) == 3 || (x & 7) == 6) + x += 4; + x = ((x >> 2) + (exp << 23)) | (uint32_t)sgn << 31; + } + memcpy(&fx, &x, 4); + return fx; +} + +double __trunctfdf2(long double f) +{ + u128_t mnt; + int32_t exp; + int sgn; + uint64_t x; + double fx; + + f3_unpack(&sgn, &exp, &mnt, f); + + if (exp == 32767 && (mnt.x0 | mnt.x1 << 16)) + x = (0x7ff8000000000000 | (uint64_t)sgn << 63 | + mnt.x1 << 16 >> 12 | mnt.x0 >> 60); + else if (exp > 17406) + x = 0x7ff0000000000000 | (uint64_t)sgn << 63; + else if (exp < 15308) + x = (uint64_t)sgn << 63; + else { + exp -= 15361; + x = mnt.x1 << 6 | mnt.x0 >> 58 | !!(mnt.x0 << 6); + if (exp < 0) { + x = x >> -exp | !!(x << (64 + exp)); + exp = 0; + } + if ((x & 3) == 3 || (x & 7) == 6) + x += 4; + x = ((x >> 2) + ((uint64_t)exp << 52)) | (uint64_t)sgn << 63; + } + memcpy(&fx, &x, 8); + return fx; +} + +int32_t __fixtfsi(long double fa) +{ + u128_t a; + int32_t a_exp; + int a_sgn; + int32_t x; + f3_unpack(&a_sgn, &a_exp, &a, fa); + if (a_exp < 16369) + return 0; + if (a_exp > 16413) + return a_sgn ? -0x80000000 : 0x7fffffff; + x = a.x1 >> (16431 - a_exp); + return a_sgn ? -x : x; +} + +int64_t __fixtfdi(long double fa) +{ + u128_t a; + int32_t a_exp; + int a_sgn; + int64_t x; + f3_unpack(&a_sgn, &a_exp, &a, fa); + if (a_exp < 16383) + return 0; + if (a_exp > 16445) + return a_sgn ? -0x8000000000000000 : 0x7fffffffffffffff; + x = (a.x1 << 15 | a.x0 >> 49) >> (16446 - a_exp); + return a_sgn ? -x : x; +} + +uint32_t __fixunstfsi(long double fa) +{ + u128_t a; + int32_t a_exp; + int a_sgn; + f3_unpack(&a_sgn, &a_exp, &a, fa); + if (a_sgn || a_exp < 16369) + return 0; + if (a_exp > 16414) + return -1; + return a.x1 >> (16431 - a_exp); +} + +uint64_t __fixunstfdi(long double fa) +{ + u128_t a; + int32_t a_exp; + int a_sgn; + f3_unpack(&a_sgn, &a_exp, &a, fa); + if (a_sgn || a_exp < 16383) + return 0; + if (a_exp > 16446) + return -1; + return (a.x1 << 15 | a.x0 >> 49) >> (16446 - a_exp); +} + +long double __floatsitf(int32_t a) +{ + int sgn = 0; + int exp = 16414; + uint32_t mnt = a; + u128_t x = { 0, 0 }; + long double f; + int i; + if (a) { + if (a < 0) { + sgn = 1; + mnt = -mnt; + } + for (i = 16; i; i >>= 1) + if (!(mnt >> (32 - i))) { + mnt <<= i; + exp -= i; + } + x.x1 = ((uint64_t)sgn << 63 | (uint64_t)exp << 48 | + (uint64_t)(mnt << 1) << 16); + } + memcpy(&f, &x, 16); + return f; +} + +long double __floatditf(int64_t a) +{ + int sgn = 0; + int exp = 16446; + uint64_t mnt = a; + u128_t x = { 0, 0 }; + long double f; + int i; + if (a) { + if (a < 0) { + sgn = 1; + mnt = -mnt; + } + for (i = 32; i; i >>= 1) + if (!(mnt >> (64 - i))) { + mnt <<= i; + exp -= i; + } + x.x0 = mnt << 49; + x.x1 = (uint64_t)sgn << 63 | (uint64_t)exp << 48 | mnt << 1 >> 16; + } + memcpy(&f, &x, 16); + return f; +} + +long double __floatunsitf(uint32_t a) +{ + int exp = 16414; + uint32_t mnt = a; + u128_t x = { 0, 0 }; + long double f; + int i; + if (a) { + for (i = 16; i; i >>= 1) + if (!(mnt >> (32 - i))) { + mnt <<= i; + exp -= i; + } + x.x1 = (uint64_t)exp << 48 | (uint64_t)(mnt << 1) << 16; + } + memcpy(&f, &x, 16); + return f; +} + +long double __floatunditf(uint64_t a) +{ + int exp = 16446; + uint64_t mnt = a; + u128_t x = { 0, 0 }; + long double f; + int i; + if (a) { + for (i = 32; i; i >>= 1) + if (!(mnt >> (64 - i))) { + mnt <<= i; + exp -= i; + } + x.x0 = mnt << 49; + x.x1 = (uint64_t)exp << 48 | mnt << 1 >> 16; + } + memcpy(&f, &x, 16); + return f; +} + +static int f3_cmp(long double fa, long double fb) +{ + u128_t a, b; + memcpy(&a, &fa, 16); + memcpy(&b, &fb, 16); + return (!(a.x0 | a.x1 << 1 | b.x0 | b.x1 << 1) ? 0 : + ((a.x1 << 1 >> 49 == 0x7fff && (a.x0 | a.x1 << 16)) || + (b.x1 << 1 >> 49 == 0x7fff && (b.x0 | b.x1 << 16))) ? 2 : + a.x1 >> 63 != b.x1 >> 63 ? (int)(b.x1 >> 63) - (int)(a.x1 >> 63) : + a.x1 < b.x1 ? (int)(a.x1 >> 63 << 1) - 1 : + a.x1 > b.x1 ? 1 - (int)(a.x1 >> 63 << 1) : + a.x0 < b.x0 ? (int)(a.x1 >> 63 << 1) - 1 : + b.x0 < a.x0 ? 1 - (int)(a.x1 >> 63 << 1) : 0); +} + +int __eqtf2(long double a, long double b) +{ + return !!f3_cmp(a, b); +} + +int __netf2(long double a, long double b) +{ + return !!f3_cmp(a, b); +} + +int __lttf2(long double a, long double b) +{ + return f3_cmp(a, b); +} + +int __letf2(long double a, long double b) +{ + return f3_cmp(a, b); +} + +int __gttf2(long double a, long double b) +{ + return -f3_cmp(b, a); +} + +int __getf2(long double a, long double b) +{ + return -f3_cmp(b, a); +} diff --git a/tinycc/lib/libtcc1.c b/tinycc/lib/libtcc1.c new file mode 100644 index 0000000..ae94af1 --- /dev/null +++ b/tinycc/lib/libtcc1.c @@ -0,0 +1,641 @@ +/* TCC runtime library. + Parts of this code are (c) 2002 Fabrice Bellard + + Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#define W_TYPE_SIZE 32 +#define BITS_PER_UNIT 8 + +typedef int Wtype; +typedef unsigned int UWtype; +typedef unsigned int USItype; +typedef long long DWtype; +typedef unsigned long long UDWtype; + +struct DWstruct { + Wtype low, high; +}; + +typedef union +{ + struct DWstruct s; + DWtype ll; +} DWunion; + +typedef long double XFtype; +#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) +#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) + +/* the following deal with IEEE single-precision numbers */ +#define EXCESS 126 +#define SIGNBIT 0x80000000 +#define HIDDEN (1 << 23) +#define SIGN(fp) ((fp) & SIGNBIT) +#define EXP(fp) (((fp) >> 23) & 0xFF) +#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) +#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) + +/* the following deal with IEEE double-precision numbers */ +#define EXCESSD 1022 +#define HIDDEND (1 << 20) +#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) +#define SIGND(fp) ((fp.l.upper) & SIGNBIT) +#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \ + (fp.l.lower >> 22)) +#define HIDDEND_LL ((long long)1 << 52) +#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) +#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) + +/* the following deal with x86 long double-precision numbers */ +#define EXCESSLD 16382 +#define EXPLD(fp) (fp.l.upper & 0x7fff) +#define SIGNLD(fp) ((fp.l.upper) & 0x8000) + +/* only for x86 */ +union ldouble_long { + long double ld; + struct { + unsigned long long lower; + unsigned short upper; + } l; +}; + +union double_long { + double d; +#if 1 + struct { + unsigned int lower; + int upper; + } l; +#else + struct { + int upper; + unsigned int lower; + } l; +#endif + long long ll; +}; + +union float_long { + float f; + unsigned int l; +}; + +/* XXX: we don't support several builtin supports for now */ +#if !defined __x86_64__ && !defined __arm__ + +/* XXX: use gcc/tcc intrinsic ? */ +#if defined __i386__ +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("mull %3" \ + : "=a" ((USItype) (w0)), \ + "=d" ((USItype) (w1)) \ + : "%0" ((USItype) (u)), \ + "rm" ((USItype) (v))) +#define udiv_qrnnd(q, r, n1, n0, dv) \ + __asm__ ("divl %4" \ + : "=a" ((USItype) (q)), \ + "=d" ((USItype) (r)) \ + : "0" ((USItype) (n0)), \ + "1" ((USItype) (n1)), \ + "rm" ((USItype) (dv))) +#define count_leading_zeros(count, x) \ + do { \ + USItype __cbtmp; \ + __asm__ ("bsrl %1,%0" \ + : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ + (count) = __cbtmp ^ 31; \ + } while (0) +#else +#error unsupported CPU type +#endif + +/* most of this code is taken from libgcc2.c from gcc */ + +static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) +{ + DWunion ww; + DWunion nn, dd; + DWunion rr; + UWtype d0, d1, n0, n1, n2; + UWtype q0, q1; + UWtype b, bm; + + nn.ll = n; + dd.ll = d; + + d0 = dd.s.low; + d1 = dd.s.high; + n0 = nn.s.low; + n1 = nn.s.high; + +#if !defined(UDIV_NEEDS_NORMALIZATION) + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + udiv_qrnnd (q1, n1, 0, n1, d0); + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0. */ + } + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = 0; + *rp = rr.ll; + } + } + +#else /* UDIV_NEEDS_NORMALIZATION */ + + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + count_leading_zeros (bm, d0); + + if (bm != 0) + { + /* Normalize, i.e. make the most significant bit of the + denominator set. */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + count_leading_zeros (bm, d0); + + if (bm == 0) + { + /* From (n1 >= d0) /\ (the most significant bit of d0 is set), + conclude (the most significant bit of n1 is set) /\ (the + leading quotient digit q1 = 1). + + This special case is necessary, not an optimization. + (Shifts counts of W_TYPE_SIZE are undefined.) */ + + n1 -= d0; + q1 = 1; + } + else + { + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + if (rp != 0) + { + rr.s.low = n0 >> bm; + rr.s.high = 0; + *rp = rr.ll; + } + } +#endif /* UDIV_NEEDS_NORMALIZATION */ + + else + { + if (d1 > n1) + { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + /* 0q = NN / dd */ + + count_leading_zeros (bm, d1); + if (bm == 0) + { + /* From (n1 >= d1) /\ (the most significant bit of d1 is set), + conclude (the most significant bit of n1 is set) /\ (the + quotient digit q0 = 0 or 1). + + This special case is necessary, not an optimization. */ + + /* The condition on the next line takes advantage of that + n1 >= d1 (true due to program flow). */ + if (n1 > d1 || n0 >= d0) + { + q0 = 1; + sub_ddmmss (n1, n0, n1, n0, d1, d0); + } + else + q0 = 0; + + q1 = 0; + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + UWtype m1, m0; + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q0, n1, n2, n1, d1); + umul_ppmm (m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) + { + q0--; + sub_ddmmss (m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + if (rp != 0) + { + sub_ddmmss (n1, n0, n1, n0, m1, m0); + rr.s.low = (n1 << b) | (n0 >> bm); + rr.s.high = n1 >> bm; + *rp = rr.ll; + } + } + } + } + + ww.s.low = q0; + ww.s.high = q1; + return ww.ll; +} + +#define __negdi2(a) (-(a)) + +long long __divdi3(long long u, long long v) +{ + int c = 0; + DWunion uu, vv; + DWtype w; + + uu.ll = u; + vv.ll = v; + + if (uu.s.high < 0) { + c = ~c; + uu.ll = __negdi2 (uu.ll); + } + if (vv.s.high < 0) { + c = ~c; + vv.ll = __negdi2 (vv.ll); + } + w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); + if (c) + w = __negdi2 (w); + return w; +} + +long long __moddi3(long long u, long long v) +{ + int c = 0; + DWunion uu, vv; + DWtype w; + + uu.ll = u; + vv.ll = v; + + if (uu.s.high < 0) { + c = ~c; + uu.ll = __negdi2 (uu.ll); + } + if (vv.s.high < 0) + vv.ll = __negdi2 (vv.ll); + + __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w); + if (c) + w = __negdi2 (w); + return w; +} + +unsigned long long __udivdi3(unsigned long long u, unsigned long long v) +{ + return __udivmoddi4 (u, v, (UDWtype *) 0); +} + +unsigned long long __umoddi3(unsigned long long u, unsigned long long v) +{ + UDWtype w; + + __udivmoddi4 (u, v, &w); + return w; +} + +/* XXX: fix tcc's code generator to do this instead */ +long long __ashrdi3(long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.low = u.s.high >> (b - 32); + u.s.high = u.s.high >> 31; + } else if (b != 0) { + u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); + u.s.high = u.s.high >> b; + } + return u.ll; +#else + return a >> b; +#endif +} + +/* XXX: fix tcc's code generator to do this instead */ +unsigned long long __lshrdi3(unsigned long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.low = (unsigned)u.s.high >> (b - 32); + u.s.high = 0; + } else if (b != 0) { + u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); + u.s.high = (unsigned)u.s.high >> b; + } + return u.ll; +#else + return a >> b; +#endif +} + +/* XXX: fix tcc's code generator to do this instead */ +long long __ashldi3(long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.high = (unsigned)u.s.low << (b - 32); + u.s.low = 0; + } else if (b != 0) { + u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b)); + u.s.low = (unsigned)u.s.low << b; + } + return u.ll; +#else + return a << b; +#endif +} + +#endif /* !__x86_64__ */ + +/* XXX: fix tcc's code generator to do this instead */ +float __floatundisf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (float)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (float)r; + } +} + +double __floatundidf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (double)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (double)r; + } +} + +long double __floatundixf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (long double)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (long double)r; + } +} + +unsigned long long __fixunssfdi (float a1) +{ + register union float_long fl1; + register int exp; + register unsigned long long l; + + fl1.f = a1; + + if (fl1.l == 0) + return (0); + + exp = EXP (fl1.l) - EXCESS - 24; + l = MANT(fl1.l); + + if (exp >= 41) + return 1ULL << 63; + else if (exp >= 0) + l <<= exp; + else if (exp >= -23) + l >>= -exp; + else + return 0; + if (SIGN(fl1.l)) + l = (unsigned long long)-l; + return l; +} + +long long __fixsfdi (float a1) +{ + long long ret; int s; + ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1); + return s ? ret : -ret; +} + +unsigned long long __fixunsdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long long l; + + dl1.d = a1; + + if (dl1.ll == 0) + return (0); + + exp = EXPD (dl1) - EXCESSD - 53; + l = MANTD_LL(dl1); + + if (exp >= 12) + return 1ULL << 63; /* overflow result (like gcc, somewhat) */ + else if (exp >= 0) + l <<= exp; + else if (exp >= -52) + l >>= -exp; + else + return 0; + if (SIGND(dl1)) + l = (unsigned long long)-l; + return l; +} + +long long __fixdfdi (double a1) +{ + long long ret; int s; + ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1); + return s ? ret : -ret; +} + +#ifndef __arm__ +unsigned long long __fixunsxfdi (long double a1) +{ + register union ldouble_long dl1; + register int exp; + register unsigned long long l; + + dl1.ld = a1; + + if (dl1.l.lower == 0 && dl1.l.upper == 0) + return (0); + + exp = EXPLD (dl1) - EXCESSLD - 64; + l = dl1.l.lower; + if (exp > 0) + return 1ULL << 63; + if (exp < -63) + return 0; + l >>= -exp; + if (SIGNLD(dl1)) + l = (unsigned long long)-l; + return l; +} + +long long __fixxfdi (long double a1) +{ + long long ret; int s; + ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1); + return s ? ret : -ret; +} +#endif /* !ARM */ + +#if defined __x86_64__ +/* float constants used for unary minus operation */ +const float __mzerosf = -0.0; +const double __mzerodf = -0.0; +#endif + +#if defined _WIN64 +/* MSVC x64 intrinsic */ +void __faststorefence(void) +{ + __asm__("lock; orl $0,(%rsp)"); +} +#endif diff --git a/tinycc/lib/stdatomic.c b/tinycc/lib/stdatomic.c new file mode 100644 index 0000000..bd8c0ce --- /dev/null +++ b/tinycc/lib/stdatomic.c @@ -0,0 +1,166 @@ +// for libtcc1, avoid including files that are not part of tcc +// #include <stdint.h> +#define uint8_t unsigned char +#define uint16_t unsigned short +#define uint32_t unsigned int +#define uint64_t unsigned long long +#define bool _Bool +#define false 0 +#define true 1 +#define __ATOMIC_RELAXED 0 +#define __ATOMIC_CONSUME 1 +#define __ATOMIC_ACQUIRE 2 +#define __ATOMIC_RELEASE 3 +#define __ATOMIC_ACQ_REL 4 +#define __ATOMIC_SEQ_CST 5 +typedef __SIZE_TYPE__ size_t; + +#if defined __i386__ || defined __x86_64__ +#define ATOMIC_COMPARE_EXCHANGE(TYPE, MODE, SUFFIX) \ + bool __atomic_compare_exchange_##MODE \ + (volatile void *atom, void *ref, TYPE xchg, \ + bool weak, int success_memorder, int failure_memorder) \ + { \ + TYPE rv; \ + TYPE cmp = *(TYPE *)ref; \ + __asm__ volatile( \ + "lock cmpxchg" SUFFIX " %2,%1\n" \ + : "=a" (rv), "+m" (*(TYPE *)atom) \ + : "q" (xchg), "0" (cmp) \ + : "memory" \ + ); \ + *(TYPE *)ref = rv; \ + return (rv == cmp); \ + } +#else +#define ATOMIC_COMPARE_EXCHANGE(TYPE, MODE, SUFFIX) \ + extern bool __atomic_compare_exchange_##MODE \ + (volatile void *atom, void *ref, TYPE xchg, \ + bool weak, int success_memorder, int failure_memorder); +#endif + +#define ATOMIC_LOAD(TYPE, MODE) \ + TYPE __atomic_load_##MODE(const volatile void *atom, int memorder) \ + { \ + return *(volatile TYPE *)atom; \ + } + +#define ATOMIC_STORE(TYPE, MODE) \ + void __atomic_store_##MODE(volatile void *atom, TYPE value, int memorder) \ + { \ + *(volatile TYPE *)atom = value; \ + } + +#define ATOMIC_GEN_OP(TYPE, MODE, NAME, OP, RET) \ + TYPE __atomic_##NAME##_##MODE(volatile void *atom, TYPE value, int memorder) \ + { \ + TYPE xchg, cmp; \ + __atomic_load((TYPE *)atom, (TYPE *)&cmp, __ATOMIC_RELAXED); \ + do { \ + xchg = (OP); \ + } while (!__atomic_compare_exchange((TYPE *)atom, &cmp, &xchg, true, \ + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)); \ + return RET; \ + } + +#define ATOMIC_EXCHANGE(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, exchange, value, cmp) +#define ATOMIC_ADD_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, add_fetch, (cmp + value), xchg) +#define ATOMIC_SUB_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, sub_fetch, (cmp - value), xchg) +#define ATOMIC_AND_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, and_fetch, (cmp & value), xchg) +#define ATOMIC_OR_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, or_fetch, (cmp | value), xchg) +#define ATOMIC_XOR_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, xor_fetch, (cmp ^ value), xchg) +#define ATOMIC_NAND_FETCH(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, nand_fetch, ~(cmp & value), xchg) +#define ATOMIC_FETCH_ADD(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_add, (cmp + value), cmp) +#define ATOMIC_FETCH_SUB(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_sub, (cmp - value), cmp) +#define ATOMIC_FETCH_AND(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_and, (cmp & value), cmp) +#define ATOMIC_FETCH_OR(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_or, (cmp | value), cmp) +#define ATOMIC_FETCH_XOR(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_xor, (cmp ^ value), cmp) +#define ATOMIC_FETCH_NAND(TYPE, MODE) \ + ATOMIC_GEN_OP(TYPE, MODE, fetch_nand, ~(cmp & value), cmp) + +#define ATOMIC_GEN(TYPE, SIZE, SUFFIX) \ + ATOMIC_STORE(TYPE, SIZE) \ + ATOMIC_LOAD(TYPE, SIZE) \ + ATOMIC_COMPARE_EXCHANGE(TYPE, SIZE, SUFFIX) \ + ATOMIC_EXCHANGE(TYPE, SIZE) \ + ATOMIC_ADD_FETCH(TYPE, SIZE) \ + ATOMIC_SUB_FETCH(TYPE, SIZE) \ + ATOMIC_AND_FETCH(TYPE, SIZE) \ + ATOMIC_OR_FETCH(TYPE, SIZE) \ + ATOMIC_XOR_FETCH(TYPE, SIZE) \ + ATOMIC_NAND_FETCH(TYPE, SIZE) \ + ATOMIC_FETCH_ADD(TYPE, SIZE) \ + ATOMIC_FETCH_SUB(TYPE, SIZE) \ + ATOMIC_FETCH_AND(TYPE, SIZE) \ + ATOMIC_FETCH_OR(TYPE, SIZE) \ + ATOMIC_FETCH_XOR(TYPE, SIZE) \ + ATOMIC_FETCH_NAND(TYPE, SIZE) + +ATOMIC_GEN(uint8_t, 1, "b") +ATOMIC_GEN(uint16_t, 2, "w") +ATOMIC_GEN(uint32_t, 4, "l") +#if defined __x86_64__ || defined __aarch64__ || defined __riscv +ATOMIC_GEN(uint64_t, 8, "q") +#endif + +/* uses alias to allow building with gcc/clang */ +#ifdef __TINYC__ +#define ATOMIC(x) __atomic_##x +#else +#define ATOMIC(x) __tcc_atomic_##x +#endif + +void ATOMIC(signal_fence) (int memorder) +{ +} + +void ATOMIC(thread_fence) (int memorder) +{ +#if defined __i386__ + __asm__ volatile("lock orl $0, (%esp)"); +#elif defined __x86_64__ + __asm__ volatile("lock orq $0, (%rsp)"); +#elif defined __arm__ + __asm__ volatile(".int 0xee070fba"); // mcr p15, 0, r0, c7, c10, 5 +#elif defined __aarch64__ + __asm__ volatile(".int 0xd5033bbf"); // dmb ish +#elif defined __riscv + __asm__ volatile(".int 0x0ff0000f"); // fence iorw,iorw +#endif +} + +bool ATOMIC(is_lock_free) (unsigned long size, const volatile void *ptr) +{ + bool ret; + + switch (size) { + case 1: ret = true; break; + case 2: ret = true; break; + case 4: ret = true; break; +#if defined __x86_64__ || defined __aarch64__ || defined __riscv + case 8: ret = true; break; +#else + case 8: ret = false; break; +#endif + default: ret = false; break; + } + return ret; +} + +#ifndef __TINYC__ +void __atomic_signal_fence(int memorder) __attribute__((alias("__tcc_atomic_signal_fence"))); +void __atomic_thread_fence(int memorder) __attribute__((alias("__tcc_atomic_thread_fence"))); +bool __atomic_is_lock_free(unsigned long size, const volatile void *ptr) __attribute__((alias("__tcc_atomic_is_lock_free"))); +#endif diff --git a/tinycc/lib/tcov.c b/tinycc/lib/tcov.c new file mode 100644 index 0000000..32b7d18 --- /dev/null +++ b/tinycc/lib/tcov.c @@ -0,0 +1,428 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#ifndef _WIN32 +#include <unistd.h> +#include <errno.h> +#else +#include <windows.h> +#include <io.h> +#endif + +/* section layout (all little endian): + 32bit offset to executable/so file name + filename \0 + function name \0 + align to 64 bits + 64bit function start line + 64bits end_line(28bits) / start_line(28bits) / flag=0xff(8bits) + 64bits counter + \0 + \0 + \0 + executable/so file name \0 + */ + +typedef struct tcov_line { + unsigned int fline; + unsigned int lline; + unsigned long long count; +} tcov_line; + +typedef struct tcov_function { + char *function; + unsigned int first_line; + unsigned int n_line; + unsigned int m_line; + tcov_line *line; +} tcov_function; + +typedef struct tcov_file { + char *filename; + unsigned int n_func; + unsigned int m_func; + tcov_function *func; + struct tcov_file *next; +} tcov_file; + +static FILE *open_tcov_file (char *cov_filename) +{ + int fd; +#ifndef _WIN32 + struct flock lock; + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; /* Until EOF. */ + lock.l_pid = getpid (); +#endif + fd = open (cov_filename, O_RDWR | O_CREAT, 0666); + if (fd < 0) + return NULL; + +#ifndef _WIN32 + while (fcntl (fd, F_SETLKW, &lock) && errno == EINTR) + continue; +#else + { + OVERLAPPED overlapped = { 0 }; + LockFileEx((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK, + 0, 1, 0, &overlapped); + } +#endif + + return fdopen (fd, "r+"); +} + +static unsigned long long get_value(unsigned char *p, int size) +{ + unsigned long long value = 0; + + p += size; + while (size--) + value = (value << 8) | *--p; + return value; +} + +static int sort_func (const void *p, const void *q) +{ + const tcov_function *pp = (const tcov_function *) p; + const tcov_function *pq = (const tcov_function *) q; + + return pp->first_line > pq->first_line ? 1 : + pp->first_line < pq->first_line ? -1 : 0; +} + +static int sort_line (const void *p, const void *q) +{ + const tcov_line *pp = (const tcov_line *) p; + const tcov_line *pq = (const tcov_line *) q; + + return pp->fline > pq->fline ? 1 : + pp->fline < pq->fline ? -1 : + pp->count < pq->count ? 1 : + pp->count > pq->count ? -1 : 0; +} + +/* sort to let inline functions work */ +static tcov_file *sort_test_coverage (unsigned char *p) +{ + int i, j, k; + unsigned char *start = p; + tcov_file *file = NULL; + tcov_file *nfile; + + p += 4; + while (*p) { + char *filename = (char *)p; + size_t len = strlen (filename); + + nfile = file; + while (nfile) { + if (strcmp (nfile->filename, filename) == 0) + break; + nfile = nfile->next; + } + if (nfile == NULL) { + nfile = malloc (sizeof(tcov_file)); + if (nfile == NULL) { + fprintf (stderr, "Malloc error test_coverage\n"); + return file; + } + nfile->filename = filename; + nfile->n_func = 0; + nfile->m_func = 0; + nfile->func = NULL; + nfile->next = NULL; + if (file == NULL) + file = nfile; + else { + tcov_file *lfile = file; + + while (lfile->next) + lfile = lfile->next; + lfile->next = nfile; + } + } + p += len + 1; + while (*p) { + int i; + char *function = (char *)p; + tcov_function *func; + + p += strlen (function) + 1; + p += -(p - start) & 7; + for (i = 0; i < nfile->n_func; i++) { + func = &nfile->func[i]; + if (strcmp (func->function, function) == 0) + break; + } + if (i == nfile->n_func) { + if (nfile->n_func >= nfile->m_func) { + nfile->m_func = nfile->m_func == 0 ? 4 : nfile->m_func * 2; + nfile->func = realloc (nfile->func, + nfile->m_func * + sizeof (tcov_function)); + if (nfile->func == NULL) { + fprintf (stderr, "Realloc error test_coverage\n"); + return file; + } + } + func = &nfile->func[nfile->n_func++]; + func->function = function; + func->first_line = get_value (p, 8); + func->n_line = 0; + func->m_line = 0; + func->line = NULL; + } + p += 8; + while (*p) { + tcov_line *line; + unsigned long long val; + + if (func->n_line >= func->m_line) { + func->m_line = func->m_line == 0 ? 4 : func->m_line * 2; + func->line = realloc (func->line, + func->m_line * sizeof (tcov_line)); + if (func->line == NULL) { + fprintf (stderr, "Realloc error test_coverage\n"); + return file; + } + } + line = &func->line[func->n_line++]; + val = get_value (p, 8); + line->fline = (val >> 8) & 0xfffffffULL; + line->lline = val >> 36; + line->count = get_value (p + 8, 8); + p += 16; + } + p++; + } + p++; + } + nfile = file; + while (nfile) { + qsort (nfile->func, nfile->n_func, sizeof (tcov_function), sort_func); + for (i = 0; i < nfile->n_func; i++) { + tcov_function *func = &nfile->func[i]; + qsort (func->line, func->n_line, sizeof (tcov_line), sort_line); + } + nfile = nfile->next; + } + return file; +} + +/* merge with previous tcov file */ +static void merge_test_coverage (tcov_file *file, FILE *fp, + unsigned int *pruns) +{ + unsigned int runs; + char *p; + char str[10000]; + + *pruns = 1; + if (fp == NULL) + return; + if (fgets(str, sizeof(str), fp) && + (p = strrchr (str, ':')) && + (sscanf (p + 1, "%u", &runs) == 1)) + *pruns = runs + 1; + while (file) { + int i; + size_t len = strlen (file->filename); + + while (fgets(str, sizeof(str), fp) && + (p = strstr(str, "0:File:")) == NULL) {} + if ((p = strstr(str, "0:File:")) == NULL || + strncmp (p + strlen("0:File:"), file->filename, len) != 0 || + p[strlen("0:File:") + len] != ' ') + break; + for (i = 0; i < file->n_func; i++) { + int j; + tcov_function *func = &file->func[i]; + unsigned int next_zero = 0; + unsigned int curline = 0; + + for (j = 0; j < func->n_line; j++) { + tcov_line *line = &func->line[j]; + unsigned int fline = line->fline; + unsigned long long count; + unsigned int tmp; + char c; + + while (curline < fline && + fgets(str, sizeof(str), fp)) + if ((p = strchr(str, ':')) && + sscanf (p + 1, "%u", &tmp) == 1) + curline = tmp; + if (sscanf (str, "%llu%c\n", &count, &c) == 2) { + if (next_zero == 0) + line->count += count; + next_zero = c == '*'; + } + } + } + file = file->next; + } +} + +/* store tcov data in file */ +void __store_test_coverage (unsigned char * p) +{ + int i, j; + unsigned int files; + unsigned int funcs; + unsigned int blocks; + unsigned int blocks_run; + unsigned int runs; + char *cov_filename = (char *)p + get_value (p, 4); + FILE *fp; + char *q; + tcov_file *file; + tcov_file *nfile; + tcov_function *func; + + fp = open_tcov_file (cov_filename); + if (fp == NULL) { + fprintf (stderr, "Cannot create coverage file: %s\n", cov_filename); + return; + } + file = sort_test_coverage (p); + merge_test_coverage (file, fp, &runs); + fseek (fp, 0, SEEK_SET); + fprintf (fp, " -: 0:Runs:%u\n", runs); + files = 0; + funcs = 0; + blocks = 0; + blocks_run = 0; + nfile = file; + while (nfile) { + files++; + for (i = 0; i < nfile->n_func; i++) { + func = &nfile->func[i]; + funcs++; + for (j = 0; j < func->n_line; j++) { + blocks++; + blocks_run += func->line[j].count != 0; + } + } + nfile = nfile->next; + } + if (blocks == 0) + blocks = 1; + fprintf (fp, " -: 0:All:%s Files:%u Functions:%u %.02f%%\n", + cov_filename, files, funcs, 100.0 * (double) blocks_run / blocks); + nfile = file; + while (nfile) { + FILE *src = fopen (nfile->filename, "r"); + unsigned int curline = 1; + char str[10000]; + + if (src == NULL) + goto next; + funcs = 0; + blocks = 0; + blocks_run = 0; + for (i = 0; i < nfile->n_func; i++) { + func = &nfile->func[i]; + funcs++; + for (j = 0; j < func->n_line; j++) { + blocks++; + blocks_run += func->line[j].count != 0; + } + } + if (blocks == 0) + blocks = 1; + fprintf (fp, " -: 0:File:%s Functions:%u %.02f%%\n", + nfile->filename, funcs, 100.0 * (double) blocks_run / blocks); + for (i = 0; i < nfile->n_func; i++) { + func = &nfile->func[i]; + + while (curline < func->first_line) + if (fgets(str, sizeof(str), src)) + fprintf (fp, " -:%5u:%s", curline++, str); + blocks = 0; + blocks_run = 0; + for (j = 0; j < func->n_line; j++) { + blocks++; + blocks_run += func->line[j].count != 0; + } + if (blocks == 0) + blocks = 1; + fprintf (fp, " -: 0:Function:%s %.02f%%\n", + func->function, 100.0 * (double) blocks_run / blocks); +#if 0 + for (j = 0; j < func->n_line; j++) { + unsigned int fline = func->line[j].fline; + unsigned int lline = func->line[j].lline; + unsigned long long count = func->line[j].count; + + fprintf (fp, "%u %u %llu\n", fline, lline, count); + } +#endif + for (j = 0; j < func->n_line;) { + unsigned int fline = func->line[j].fline; + unsigned int lline = func->line[j].lline; + unsigned long long count = func->line[j].count; + unsigned int has_zero = 0; + unsigned int same_line = fline == lline; + + j++; + while (j < func->n_line) { + unsigned int nfline = func->line[j].fline; + unsigned int nlline = func->line[j].lline; + unsigned long long ncount = func->line[j].count; + + if (fline == nfline) { + if (ncount == 0) + has_zero = 1; + else if (ncount > count) + count = ncount; + same_line = nfline == nlline; + lline = nlline; + j++; + } + else + break; + } + if (same_line) + lline++; + + while (curline < fline) + if (fgets(str, sizeof(str), src)) + fprintf (fp, " -:%5u:%s", curline++, str); + while (curline < lline && + fgets(str, sizeof(str), src)) { + if (count == 0) + fprintf (fp, " #####:%5u:%s", + curline, str); + else if (has_zero) + fprintf (fp, "%8llu*:%5u:%s", + count, curline, str); + else + fprintf (fp, "%9llu:%5u:%s", + count, curline, str); + curline++; + } + } + } + while (fgets(str, sizeof(str), src)) + fprintf (fp, " -:%5u:%s", curline++, str); + fclose (src); +next: + nfile = nfile->next; + } + while (file) { + for (i = 0; i < file->n_func; i++) { + func = &file->func[i]; + free (func->line); + } + free (file->func); + nfile = file; + file = file->next; + free (nfile); + } + fclose (fp); +} diff --git a/tinycc/lib/va_list.c b/tinycc/lib/va_list.c new file mode 100644 index 0000000..1fb5512 --- /dev/null +++ b/tinycc/lib/va_list.c @@ -0,0 +1,67 @@ +/* va_list.c - tinycc support for va_list on X86_64 */ + +#if defined __x86_64__ + +/* Avoid include files, they may not be available when cross compiling */ +extern void abort(void); + +/* This should be in sync with our include/stdarg.h */ +enum __va_arg_type { + __va_gen_reg, __va_float_reg, __va_stack +}; + +/* GCC compatible definition of va_list. */ +/*predefined by TCC (tcc_predefs.h): +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + union { + unsigned int overflow_offset; + char *overflow_arg_area; + }; + char *reg_save_area; +} __builtin_va_list[1]; +*/ + +extern void *memcpy(void *dest, const void *src, unsigned long n); + +void *__va_arg(__builtin_va_list ap, + int arg_type, + int size, int align) +{ + size = (size + 7) & ~7; + align = (align + 7) & ~7; + switch ((enum __va_arg_type)arg_type) { + case __va_gen_reg: + if (ap->gp_offset + size <= 48) { + ap->gp_offset += size; + return ap->reg_save_area + ap->gp_offset - size; + } + goto use_overflow_area; + + case __va_float_reg: + if (ap->fp_offset < 128 + 48) { + ap->fp_offset += 16; + if (size == 8) + return ap->reg_save_area + ap->fp_offset - 16; + if (ap->fp_offset < 128 + 48) { + memcpy(ap->reg_save_area + ap->fp_offset - 8, + ap->reg_save_area + ap->fp_offset, 8); + ap->fp_offset += 16; + return ap->reg_save_area + ap->fp_offset - 32; + } + } + goto use_overflow_area; + + case __va_stack: + use_overflow_area: + ap->overflow_arg_area += size; + ap->overflow_arg_area = (char*)((long long)(ap->overflow_arg_area + align - 1) & -align); + return ap->overflow_arg_area - size; + + default: /* should never happen */ + abort(); + return 0; + } +} +#endif |
