diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 77 | ||||
-rw-r--r-- | lib/alloca86-bt.S | 13 | ||||
-rw-r--r-- | lib/alloca86.S | 9 | ||||
-rw-r--r-- | lib/alloca86_64-bt.S | 47 | ||||
-rw-r--r-- | lib/alloca86_64.S | 10 | ||||
-rw-r--r-- | lib/armflush.c | 8 | ||||
-rw-r--r-- | lib/bcheck.c | 2294 | ||||
-rw-r--r-- | lib/bt-dll.c | 73 | ||||
-rw-r--r-- | lib/bt-exe.c | 43 | ||||
-rw-r--r-- | lib/bt-log.c | 37 | ||||
-rw-r--r-- | lib/dsohandle.c | 1 | ||||
-rw-r--r-- | lib/fetch_and_add_arm.S | 28 | ||||
-rw-r--r-- | lib/fetch_and_add_arm64.S | 22 | ||||
-rw-r--r-- | lib/fetch_and_add_riscv64.S | 16 | ||||
-rw-r--r-- | lib/lib-arm64.c | 17 | ||||
-rw-r--r-- | lib/va_list.c | 20 |
16 files changed, 1932 insertions, 783 deletions
diff --git a/lib/Makefile b/lib/Makefile index 0c1ec54..bc57be7 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -7,67 +7,86 @@ include $(TOP)/Makefile VPATH = $(TOPSRC)/lib $(TOPSRC)/win32/lib T = $(or $(CROSS_TARGET),$(NATIVE_TARGET),unknown) X = $(if $(CROSS_TARGET),$(CROSS_TARGET)-) -BIN = $(TOP)/$(X)libtcc1.a 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)) +XFLAGS = $(XFLAGS$(XCFG)) -I$(TOP) XCFG = $(or $(findstring -win,$T),-unx) +S = $(if $(findstring yes,$(SILENT)),@$(info * $@)) -# in order to use gcc, tyoe: make <target>-libtcc1-usegcc=yes +# 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 + XFLAGS = $(CFLAGS) -fPIC -gstabs -fno-omit-frame-pointer -Wno-unused-function -Wno-unused-variable endif # only for native compiler $(X)BCHECK_O = bcheck.o +$(X)BT_O = bt-exe.o bt-log.o +$(X)B_O = bcheck.o bt-exe.o bt-log.o bt-dll.o ifeq ($(CONFIG_musl)$(CONFIG_uClibc),yes) BCHECK_O = +else + DSO_O = dsohandle.o endif -ifdef CONFIG_OSX - XFLAGS += -D_ANSI_SOURCE -endif - -I386_O = libtcc1.o alloca86.o alloca86-bt.o -X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o -ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o -ARM64_O = lib-arm64.o +I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BT_O) +X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o $(BT_O) +ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o fetch_and_add_arm.o $(BT_O) +ARM64_O = lib-arm64.o fetch_and_add_arm64.o $(BT_O) +RISCV64_O = lib-arm64.o fetch_and_add_riscv64.o $(BT_O) WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o -OBJ-i386 = $(I386_O) $(BCHECK_O) -OBJ-x86_64 = $(X86_64_O) va_list.o $(BCHECK_O) -OBJ-x86_64-osx = $(X86_64_O) va_list.o -OBJ-i386-win32 = $(I386_O) chkstk.o bcheck.o $(WIN_O) -OBJ-x86_64-win32 = $(X86_64_O) chkstk.o bcheck.o $(WIN_O) -OBJ-arm64 = $(ARM64_O) -OBJ-arm = $(ARM_O) -OBJ-arm-fpa = $(ARM_O) -OBJ-arm-fpa-ld = $(ARM_O) -OBJ-arm-vfp = $(ARM_O) -OBJ-arm-eabi = $(ARM_O) -OBJ-arm-eabihf = $(ARM_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-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))) -$(BIN) : $(patsubst %.o,$(X)%.o,$(OBJ-$T)) - $(XAR) rcs $@ $^ +ALL = $(addprefix $(TOP)/,$(X)libtcc1.a $(OBJ-extra)) + +all: $(ALL) + +$(TOP)/$(X)libtcc1.a : $(OBJ-libtcc1) + $S$(XAR) rcs $@ $^ $(X)%.o : %.c - $(XCC) -c $< -o $@ $(XFLAGS) + $S$(XCC) -c $< -o $@ $(XFLAGS) $(X)%.o : %.S - $(XCC) -c $< -o $@ $(XFLAGS) + $S$(XCC) -c $< -o $@ $(XFLAGS) + +$(TOP)/%.o : %.c + $S$(XCC) -c $< -o $@ $(XFLAGS) + +$(TOP)/bcheck.o : XFLAGS += -g +$(TOP)/bt-exe.o : $(TOP)/tccrun.c $(X)crt1w.o : crt1.c $(X)wincrt1w.o : wincrt1.c clean : - rm -f *.a *.o $(BIN) + rm -f *.a *.o $(ALL) diff --git a/lib/alloca86-bt.S b/lib/alloca86-bt.S index 4f95cf1..177838a 100644 --- a/lib/alloca86-bt.S +++ b/lib/alloca86-bt.S @@ -1,9 +1,14 @@ /* ---------------------------------------------- */ /* alloca86-bt.S */ -.globl __bound_alloca +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif -__bound_alloca: +.globl _(__bound_alloca) +_(__bound_alloca): pop %edx pop %eax mov %eax, %ecx @@ -30,8 +35,8 @@ p5: push %eax push %ecx push %eax - call __bound_new_region - add $8, %esp + call _(__bound_new_region) + add $8, %esp pop %eax pop %edx diff --git a/lib/alloca86.S b/lib/alloca86.S index bb7a2c2..bdc7391 100644 --- a/lib/alloca86.S +++ b/lib/alloca86.S @@ -1,9 +1,14 @@ /* ---------------------------------------------- */ /* alloca86.S */ -.globl alloca +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif -alloca: +.globl _(alloca) +_(alloca): pop %edx pop %eax add $3,%eax diff --git a/lib/alloca86_64-bt.S b/lib/alloca86_64-bt.S index 4cbad90..d1df3a9 100644 --- a/lib/alloca86_64-bt.S +++ b/lib/alloca86_64-bt.S @@ -1,39 +1,34 @@ /* ---------------------------------------------- */ /* alloca86_64.S */ -.globl __bound_alloca -__bound_alloca: +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif +.globl _(__bound_alloca) +_(__bound_alloca): #ifdef _WIN32 - # bound checking is not implemented - pop %rdx - mov %rcx,%rax - add $15,%rax - and $-16,%rax - jz p3 - -p1: - cmp $4096,%rax - jbe p2 - test %rax,-4096(%rsp) - sub $4096,%rsp - sub $4096,%rax - jmp p1 -p2: - - sub %rax,%rsp - mov %rsp,%rax - add $32,%rax - -p3: - push %rdx + 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,%rax + add $15 + 1,%rax # add one extra to separate regions and $-16,%rax jz p3 @@ -44,7 +39,7 @@ p3: push %rdx push %rax - call __bound_new_region + call _(__bound_new_region) pop %rax pop %rdx diff --git a/lib/alloca86_64.S b/lib/alloca86_64.S index ae3c97d..5195eca 100644 --- a/lib/alloca86_64.S +++ b/lib/alloca86_64.S @@ -1,9 +1,14 @@ /* ---------------------------------------------- */ /* alloca86_64.S */ -.globl alloca +#ifdef __leading_underscore +# define _(s) _##s +#else +# define _(s) s +#endif -alloca: +.globl _(alloca) +_(alloca): pop %rdx #ifdef _WIN32 mov %rcx,%rax @@ -24,7 +29,6 @@ p1: jmp p1 p2: #endif - sub %rax,%rsp mov %rsp,%rax p3: diff --git a/lib/armflush.c b/lib/armflush.c index eae3260..13955e1 100644 --- a/lib/armflush.c +++ b/lib/armflush.c @@ -7,12 +7,12 @@ #ifdef __TINYC__ /* syscall wrapper */ -unsigned syscall(unsigned syscall_nr, ...); +unsigned _tccsyscall(unsigned syscall_nr, ...); /* arm-tcc supports only fake asm currently */ __asm__( - ".global syscall\n" - "syscall:\n" + ".global _tccsyscall\n" + "_tccsyscall:\n" ".int 0xe92d4080\n" // push {r7, lr} ".int 0xe1a07000\n" // mov r7, r0 ".int 0xe1a00001\n" // mov r0, r1 @@ -31,6 +31,8 @@ __asm__( #define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000) #define __ARM_NR_cacheflush (__ARM_NR_BASE+2) +#define syscall _tccsyscall + #else #define _GNU_SOURCE diff --git a/lib/bcheck.c b/lib/bcheck.c index 90f0ad2..9c0a40b 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -21,218 +21,471 @@ #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 */ +#define BOUND_DEBUG (1) +#define BOUND_STATISTIC (1) -#ifdef BOUND_DEBUG - #define dprintf(a...) fprintf(a) +#if BOUND_DEBUG + #define dprintf(a...) if (print_calls) fprintf(a) #else #define dprintf(a...) #endif -/* define so that bound array is static (faster, but use memory if - bound checking not used) */ -/* #define BOUND_STATIC */ +#ifdef __attribute__ + /* an __attribute__ macro is defined in the system headers */ + #undef __attribute__ +#endif +#define FASTCALL __attribute__((regparm(3))) -/* use malloc hooks. Currently the code cannot be reliable if no hooks */ -#define CONFIG_TCC_MALLOC_HOOKS -#define HAVE_MEMALIGN +#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__) \ - || defined(_WIN32) -//#warning Bound checking does not support malloc (etc.) in this environment. -#undef CONFIG_TCC_MALLOC_HOOKS -#undef HAVE_MEMALIGN -#endif + || defined(__dietlibc__) + +#define INIT_SEM() +#define EXIT_SEM() +#define WAIT_SEM() +#define POST_SEM() +#define HAVE_MEMALIGN (0) +#define MALLOC_REDIR (0) +#define HAVE_PTHREAD_CREATE (0) +#define HAVE_CTYPE (0) +#define HAVE_ERRNO (0) + +#elif defined(_WIN32) + +#include <windows.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 HAVE_MEMALIGN (0) +#define MALLOC_REDIR (0) +#define HAVE_PTHREAD_CREATE (0) +#define HAVE_CTYPE (0) +#define HAVE_ERRNO (0) -#define BOUND_T1_BITS 13 -#define BOUND_T2_BITS 11 -#define BOUND_T3_BITS (sizeof(size_t)*8 - BOUND_T1_BITS - BOUND_T2_BITS) -#define BOUND_E_BITS (sizeof(size_t)) +#else -#define BOUND_T1_SIZE ((size_t)1 << BOUND_T1_BITS) -#define BOUND_T2_SIZE ((size_t)1 << BOUND_T2_BITS) -#define BOUND_T3_SIZE ((size_t)1 << BOUND_T3_BITS) +#define __USE_GNU /* get RTLD_NEXT */ +#include <sys/mman.h> +#include <ctype.h> +#include <pthread.h> +#include <dlfcn.h> +#include <errno.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) +#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) +#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) +#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) +#endif +#define HAVE_MEMALIGN (1) +#define MALLOC_REDIR (1) +#define HAVE_PTHREAD_CREATE (1) +#define HAVE_CTYPE (1) +#define HAVE_ERRNO (1) + +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 void *(*memalign_redir) (size_t, size_t); +static int (*pthread_create_redir) (pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); +static unsigned int pool_index; +static unsigned char __attribute__((aligned(16))) initial_pool[256]; +static unsigned char use_sem; -#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS) -#define BOUND_T23_SIZE ((size_t)1 << BOUND_T23_BITS) +#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)) -/* size of an empty region */ -#define EMPTY_SIZE ((size_t)(-1)) -/* size of an invalid region */ -#define INVALID_SIZE 0 -typedef struct BoundEntry { +typedef struct tree_node Tree; +struct tree_node { + Tree * left, * right; size_t start; size_t size; - struct BoundEntry *next; - size_t is_invalid; /* true if pointers outside region are invalid */ -} BoundEntry; + unsigned char type; + unsigned char is_invalid; /* true if pointers outside region are invalid */ +}; + +typedef struct alloca_list_struct { + size_t fp; + void *p; + struct alloca_list_struct *next; +} alloca_list_type; + +#if defined(_WIN32) +#define BOUND_TID_TYPE DWORD +#define BOUND_GET_TID GetCurrentThreadId() +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || defined(__riscv) +#define BOUND_TID_TYPE pid_t +#define BOUND_GET_TID syscall (SYS_gettid) +#else +#define BOUND_TID_TYPE int +#define BOUND_GET_TID 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 __bound_init(void); -void __bound_new_region(void *p, size_t size); -int __bound_delete_region(void *p); +void __bound_checking (int no_check); +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); +#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_strchr(const char *string, int ch); +DLL_EXPORT char *__bound_strdup(const char *s); + +#if defined(__arm__) +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 -#ifdef __attribute__ - /* an __attribute__ macro is defined in the system headers */ - #undef __attribute__ +#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 FASTCALL __attribute__((regparm(3))) -void *__bound_malloc(size_t size, const void *caller); -void *__bound_memalign(size_t size, size_t align, const void *caller); -void __bound_free(void *ptr, const void *caller); -void *__bound_realloc(void *ptr, size_t size, const void *caller); -static void *libc_malloc(size_t size); -static void libc_free(void *ptr); -static void install_malloc_hooks(void); -static void restore_malloc_hooks(void); +#define FREE_REUSE_SIZE (100) +static unsigned int free_reuse_index; +static void *free_reuse_list[FREE_REUSE_SIZE]; -#ifdef CONFIG_TCC_MALLOC_HOOKS -static void *saved_malloc_hook; -static void *saved_free_hook; -static void *saved_realloc_hook; -static void *saved_memalign_hook; +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 int never_fatal; +static int no_checking = 1; +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_strchr_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 -/* TCC definitions */ -extern char __bounds_start; /* start of static bounds table */ -/* error message, just for TCC */ -const char *__bound_error_msg; +/* currently only i386/x86_64 supported. Change for other platforms */ +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 +} -/* runtime error output */ -extern void rt_error(size_t pc, const char *fmt, ...); +/* enable/disable checking. This can be used in signal handlers. */ +void __bound_checking (int no_check) +{ + fetch_and_add (&no_checking, no_check); +} -#ifdef BOUND_STATIC -static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */ -#else -static BoundEntry **__bound_t1; /* page table */ -#endif -static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */ -static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */ - -static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) -{ - size_t addr, tmp; - BoundEntry *e; - - e = e1; - while (e != NULL) { - addr = (size_t)p; - addr -= e->start; - if (addr <= e->size) { - /* put region at the head */ - tmp = e1->start; - e1->start = e->start; - e->start = tmp; - tmp = e1->size; - e1->size = e->size; - e->size = tmp; - return e1; - } - e = e->next; - } - /* no entry found: return empty entry or invalid entry */ - if (e1->is_invalid) - return __bound_invalid_t2; - else - return __bound_empty_t2; +/* enable/disable checking. This can be used in signal handlers. */ +void __bound_never_fatal (int neverfatal) +{ + fetch_and_add (&never_fatal, neverfatal); } +int tcc_backtrace(const char *fmt, ...); + /* print a bound error message */ -static void bound_error(const char *fmt, ...) +#define bound_warning(...) \ + tcc_backtrace("^bcheck.c^BCHECK: " __VA_ARGS__) + +#define bound_error(...) \ + do { \ + bound_warning(__VA_ARGS__); \ + if (never_fatal == 0) \ + exit(255); \ + } while (0) + +static void bound_alloc_error(const char *s) { - __bound_error_msg = fmt; - fprintf(stderr,"%s %s: %s\n", __FILE__, __FUNCTION__, fmt); - *(void **)0 = 0; /* force a runtime error */ + fprintf(stderr,"FATAL: %s\n",s); + exit (1); } -static void bound_alloc_error(void) +static void bound_not_found_warning(const char *file, const char *function, + void *ptr) { - bound_error("not enough memory for bound checking code"); + dprintf(stderr, "%s%s, %s(): Not found %p\n", exec, file, function, ptr); } /* return '(p + offset)' for pointer arithmetic (a pointer can reach the end of a region in this case */ -void * FASTCALL __bound_ptr_add(void *p, size_t offset) +void * __bound_ptr_add(void *p, size_t offset) { size_t addr = (size_t)p; - BoundEntry *e; - dprintf(stderr, "%s %s: %p %x\n", - __FILE__, __FUNCTION__, p, (unsigned)offset); + if (no_checking) + return p + offset; - __bound_init(); + dprintf(stderr, "%s, %s(): %p 0x%lx\n", + __FILE__, __FUNCTION__, p, (unsigned long)offset); - e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; - e = (BoundEntry *)((char *)e + - ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); - addr -= e->start; - if (addr > e->size) { - e = __bound_find_region(e, p); - addr = (size_t)p - e->start; - } - addr += offset; - if (addr >= e->size) { - fprintf(stderr,"%s %s: %p is outside of the region\n", - __FILE__, __FUNCTION__, p + offset); - return INVALID_POINTER; /* return an invalid pointer */ + 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 * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \ -{ \ - size_t addr = (size_t)p; \ - BoundEntry *e; \ - \ - dprintf(stderr, "%s %s: %p %x start\n", \ - __FILE__, __FUNCTION__, p, (unsigned)offset); \ - \ - __bound_init(); \ - e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \ - e = (BoundEntry *)((char *)e + \ - ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \ - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \ - addr -= e->start; \ - if (addr > e->size) { \ - e = __bound_find_region(e, p); \ - addr = (size_t)p - e->start; \ - } \ - addr += offset + dsize; \ - if (addr > e->size) { \ - fprintf(stderr,"%s %s: %p is outside of the region\n", \ - __FILE__, __FUNCTION__, p + offset); \ - return INVALID_POINTER; /* return an invalid pointer */ \ - } \ - dprintf(stderr, "%s %s: return p+offset = %p\n", \ - __FILE__, __FUNCTION__, p + offset); \ - return p + offset; \ +#define BOUND_PTR_INDIR(dsize) \ +void * __bound_ptr_indir ## dsize (void *p, size_t offset) \ +{ \ + size_t addr = (size_t)p; \ + \ + if (no_checking) \ + 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) @@ -260,720 +513,1359 @@ BOUND_PTR_INDIR(16) /* called when entering a function to add all the local regions */ void FASTCALL __bound_local_new(void *p1) { - size_t addr, size, fp, *p = p1; + size_t addr, fp, *p = p1; - dprintf(stderr, "%s, %s start p1=%p\n", __FILE__, __FUNCTION__, p); + if (no_checking) + return; GET_CALLER_FP(fp); - for(;;) { - addr = p[0]; - if (addr == 0) - break; - addr += fp; - size = p[1]; + 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; - __bound_new_region((void *)addr, size); } - dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__); + 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) + return; GET_CALLER_FP(fp); - for(;;) { - addr = p[0]; - if (addr == 0) - break; - addr += 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; - __bound_delete_region((void *)addr); } -} - -#if defined(__GNUC__) && (__GNUC__ >= 6) -#pragma GCC diagnostic pop -#endif - -static BoundEntry *__bound_new_page(void) -{ - BoundEntry *page; - size_t i; + 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); + } - page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); - if (!page) - bound_alloc_error(); - for(i=0;i<BOUND_T2_SIZE;i++) { - /* put empty entries */ - page[i].start = 0; - page[i].size = EMPTY_SIZE; - page[i].next = NULL; - page[i].is_invalid = 0; + 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; + } } - return page; +#endif } -/* currently we use malloc(). Should use bound_new_page() */ -static BoundEntry *bound_new_entry(void) +/* used by alloca */ +void __bound_new_region(void *p, size_t size) { - BoundEntry *e; - e = libc_malloc(sizeof(BoundEntry)); - return e; -} + size_t fp; + alloca_list_type *last; + alloca_list_type *cur; + alloca_list_type *new; -static void bound_free_entry(BoundEntry *e) -{ - libc_free(e); -} + if (no_checking) + return; -static BoundEntry *get_page(size_t index) -{ - BoundEntry *page; - page = __bound_t1[index]; - if (!page || page == __bound_empty_t2 || page == __bound_invalid_t2) { - /* create a new page if necessary */ - page = __bound_new_page(); - __bound_t1[index] = page; + 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 (cur->fp == fp && cur->p == p) { + if (last) + last->next = cur->next; + else + alloca_list = cur->next; + tree = splay_delete((size_t)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->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); } - return page; } -/* mark a region as being invalid (can only be used during init) */ -static void mark_invalid(size_t addr, size_t size) +void __bound_setjmp(jmp_buf env) { - size_t start, end; - BoundEntry *page; - size_t t1_start, t1_end, i, j, t2_start, t2_end; - - start = addr; - end = addr + size; - - t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS; - if (end != 0) - t2_end = end >> BOUND_T3_BITS; - else - t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS); - -#if 0 - dprintf(stderr, "mark_invalid: start = %x %x\n", t2_start, t2_end); -#endif - - /* first we handle full pages */ - t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS; - t1_end = t2_end >> BOUND_T2_BITS; - - i = t2_start & (BOUND_T2_SIZE - 1); - j = t2_end & (BOUND_T2_SIZE - 1); - - if (t1_start == t1_end) { - page = get_page(t2_start >> BOUND_T2_BITS); - for(; i < j; i++) { - page[i].size = INVALID_SIZE; - page[i].is_invalid = 1; + jmp_list_type *jl; + void *e = (void *) env; + + if (no_checking == 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; } - } else { - if (i > 0) { - page = get_page(t2_start >> BOUND_T2_BITS); - for(; i < BOUND_T2_SIZE; i++) { - page[i].size = INVALID_SIZE; - page[i].is_invalid = 1; + if (jl == NULL) { + jl = BOUND_MALLOC (sizeof (jmp_list_type)); + if (jl) { + jl->penv = e; + jl->next = jmp_list; + jmp_list = jl; } } - for(i = t1_start; i < t1_end; i++) { - __bound_t1[i] = __bound_invalid_t2; - } - if (j != 0) { - page = get_page(t1_end); - for(i = 0; i < j; i++) { - page[i].size = INVALID_SIZE; - page[i].is_invalid = 1; - } + if (jl) { + size_t fp; + + GET_CALLER_FP (fp); + jl->fp = fp; + jl->end_fp = (size_t)__builtin_frame_address(0); + jl->tid = BOUND_GET_TID; } + POST_SEM (); } } -void __bound_init(void) +static void __bound_long_jump(jmp_buf env, int val, int sig, const char *func) { - size_t i; - BoundEntry *page; - size_t start, size; - size_t *p; - - static int inited; - if (inited) - return; - - inited = 1; - - dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__); - - /* save malloc hooks and install bound check hooks */ - install_malloc_hooks(); - -#ifndef BOUND_STATIC - __bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *)); - if (!__bound_t1) - bound_alloc_error(); -#endif - __bound_empty_t2 = __bound_new_page(); - for(i=0;i<BOUND_T1_SIZE;i++) { - __bound_t1[i] = __bound_empty_t2; - } - - page = __bound_new_page(); - for(i=0;i<BOUND_T2_SIZE;i++) { - /* put invalid entries */ - page[i].start = 0; - page[i].size = INVALID_SIZE; - page[i].next = NULL; - page[i].is_invalid = 1; - } - __bound_invalid_t2 = page; - - /* invalid pointer zone */ - start = (size_t)INVALID_POINTER & ~(BOUND_T23_SIZE - 1); - size = BOUND_T23_SIZE; - mark_invalid(start, size); - -#if defined(CONFIG_TCC_MALLOC_HOOKS) - /* malloc zone is also marked invalid. can only use that with - * hooks because all libs should use the same malloc. The solution - * would be to build a new malloc for tcc. - * - * usually heap (= malloc zone) comes right after bss, i.e. after _end, but - * not always - either if we are running from under `tcc -b -run`, or if - * address space randomization is turned on(a), heap start will be separated - * from bss end. - * - * So sbrk(0) will be a good approximation for start_brk: - * - * - if we are a separately compiled program, __bound_init() runs early, - * and sbrk(0) should be equal or very near to start_brk(b) (in case other - * constructors malloc something), or - * - * - if we are running from under `tcc -b -run`, sbrk(0) will return - * start of heap portion which is under this program control, and not - * mark as invalid earlier allocated memory. - * - * - * (a) /proc/sys/kernel/randomize_va_space = 2, on Linux; - * usually turned on by default. - * - * (b) on Linux >= v3.3, the alternative is to read - * start_brk from /proc/self/stat - */ - start = (size_t)sbrk(0); - size = 128 * 0x100000; - mark_invalid(start, size); -#endif - - /* add all static bound check values */ - p = (size_t *)&__bounds_start; - while (p[0] != 0) { - __bound_new_region((void *)p[0], p[1]); - p += 2; + jmp_list_type *jl; + void *e; + BOUND_TID_TYPE tid; + + if (no_checking == 0) { + e = (void *)env; + tid = BOUND_GET_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(); } - - dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__); +#if !defined(_WIN32) + sig ? siglongjmp(env, val) : +#endif + longjmp (env, val); } -void __bound_main_arg(void **p) +void __bound_longjmp(jmp_buf env, int val) { - void *start = p; - while (*p++); - - dprintf(stderr, "%s, %s calling __bound_new_region(%p %x)\n", - __FILE__, __FUNCTION__, start, (unsigned)((void *)p - start)); - - __bound_new_region(start, (void *) p - start); + __bound_long_jump(env,val, 0, __FUNCTION__); } -void __bound_exit(void) +#if !defined(_WIN32) +void __bound_siglongjmp(jmp_buf env, int val) { - dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); - restore_malloc_hooks(); + __bound_long_jump(env,val, 1, __FUNCTION__); } +#endif -static inline void add_region(BoundEntry *e, - size_t start, size_t size) -{ - BoundEntry *e1; - if (e->start == 0) { - /* no region : add it */ - e->start = start; - e->size = size; - } else { - /* already regions in the list: add it at the head */ - e1 = bound_new_entry(); - e1->start = e->start; - e1->size = e->size; - e1->next = e->next; - e->start = start; - e->size = size; - e->next = e1; - } -} +#if defined(__GNUC__) && (__GNUC__ >= 6) +#pragma GCC diagnostic pop +#endif -/* create a new region. It should not already exist in the region list */ -void __bound_new_region(void *p, size_t size) +void __bound_init(size_t *p, int mode) { - size_t start, end; - BoundEntry *page, *e, *e2; - size_t t1_start, t1_end, i, t2_start, t2_end; - - dprintf(stderr, "%s, %s(%p, %x) start\n", - __FILE__, __FUNCTION__, p, (unsigned)size); - - __bound_init(); - - start = (size_t)p; - end = start + size; - t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); - t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); - - /* start */ - page = get_page(t1_start); - t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); - t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); + 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; - e = (BoundEntry *)((char *)page + t2_start); - add_region(e, start, size); + 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); +#endif + } +#endif - if (t1_end == t1_start) { - /* same ending page */ - e2 = (BoundEntry *)((char *)page + t2_end); - if (e2 > e) { - e++; - for(;e<e2;e++) { - e->start = start; - e->size = size; - } - add_region(e, start, size); +#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); } - } else { - /* mark until end of page */ - e2 = page + BOUND_T2_SIZE; - e++; - for(;e<e2;e++) { - e->start = start; - e->size = size; - } - /* mark intermediate pages, if any */ - for(i=t1_start+1;i<t1_end;i++) { - page = get_page(i); - e2 = page + BOUND_T2_SIZE; - for(e=page;e<e2;e++) { - e->start = start; - e->size = size; + /* 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); } - /* last page */ - page = get_page(t1_end); - e2 = (BoundEntry *)((char *)page + t2_end); - for(e=page;e<e2;e++) { - e->start = start; - e->size = size; + if (found == 0) { + use_sem = 1; + no_strdup = 1; } - add_region(e, start, size); } +#endif - dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__); -} + WAIT_SEM (); -/* delete a region */ -static inline void delete_region(BoundEntry *e, void *p, size_t empty_size) -{ - size_t addr; - BoundEntry *e1; +#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 - addr = (size_t)p; - addr -= e->start; - if (addr <= e->size) { - /* region found is first one */ - e1 = e->next; - if (e1 == NULL) { - /* no more region: mark it empty */ - e->start = 0; - e->size = empty_size; - } else { - /* copy next region in head */ - e->start = e1->start; - e->size = e1->size; - e->next = e1->next; - bound_free_entry(e1); - } - } else { - /* find the matching region */ - for(;;) { - e1 = e; - e = e->next; - /* region not found: do nothing */ - if (e == NULL) - break; - addr = (size_t)p - e->start; - if (addr <= e->size) { - /* found: remove entry */ - e1->next = e->next; - bound_free_entry(e); - break; - } +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 = 0; + dprintf(stderr, "%s, %s(): end\n\n", __FILE__, __FUNCTION__); } -/* WARNING: 'p' must be the starting point of the region. */ -/* return non zero if error */ -int __bound_delete_region(void *p) +void +#if (defined(__GLIBC__) && (__GLIBC_MINOR__ >= 4)) || defined(_WIN32) +__attribute__((constructor)) +#endif +__bound_main_arg(int argc, char **argv, char **envp) { - size_t start, end, addr, size, empty_size; - BoundEntry *page, *e, *e2; - size_t t1_start, t1_end, t2_start, t2_end, i; - - dprintf(stderr, "%s %s() start\n", __FILE__, __FUNCTION__); + __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 0x%lx\n", + __FILE__, __FUNCTION__, argv, (argc + 1) * sizeof(char *)); + } +#endif + } - __bound_init(); + if (envp && *envp) { + char **p = envp; - start = (size_t)p; - t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); - t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); - - /* find region size */ - page = __bound_t1[t1_start]; - e = (BoundEntry *)((char *)page + t2_start); - addr = start - e->start; - if (addr > e->size) - e = __bound_find_region(e, p); - /* test if invalid region */ - if (e->size == EMPTY_SIZE || (size_t)p != e->start) - return -1; - /* compute the size we put in invalid regions */ - if (e->is_invalid) - empty_size = INVALID_SIZE; - else - empty_size = EMPTY_SIZE; - size = e->size; - end = start + size; - - /* now we can free each entry */ - t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); - t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); - - delete_region(e, p, empty_size); - if (t1_end == t1_start) { - /* same ending page */ - e2 = (BoundEntry *)((char *)page + t2_end); - if (e2 > e) { - e++; - for(;e<e2;e++) { - e->start = 0; - e->size = empty_size; - } - delete_region(e, p, empty_size); + WAIT_SEM (); + while (*p) { + tree = splay_insert((size_t) *p, strlen (*p) + 1, tree); + ++p; } - } else { - /* mark until end of page */ - e2 = page + BOUND_T2_SIZE; - e++; - for(;e<e2;e++) { - e->start = 0; - e->size = empty_size; - } - /* mark intermediate pages, if any */ - /* XXX: should free them */ - for(i=t1_start+1;i<t1_end;i++) { - page = get_page(i); - e2 = page + BOUND_T2_SIZE; - for(e=page;e<e2;e++) { - e->start = 0; - e->size = empty_size; + 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 0x%lx\n", + __FILE__, __FUNCTION__, envp, (++p - envp) * sizeof(char *)); } - /* last page */ - page = get_page(t1_end); - e2 = (BoundEntry *)((char *)page + t2_end); - for(e=page;e<e2;e++) { - e->start = 0; - e->size = empty_size; - } - delete_region(e, p, empty_size); +#endif } - - dprintf(stderr, "%s %s() end\n", __FILE__, __FUNCTION__); - - return 0; } -/* return the size of the region starting at p, or EMPTY_SIZE if non - existent region. */ -static size_t get_region_size(void *p) +void __attribute__((destructor)) __bound_exit(void) { - size_t addr = (size_t)p; - BoundEntry *e; - - e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; - e = (BoundEntry *)((char *)e + - ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & - ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); - addr -= e->start; - if (addr > e->size) - e = __bound_find_region(e, p); - if (e->start != (size_t)p) - return EMPTY_SIZE; - return e->size; -} + 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__) + if (print_heap) { + extern void __libc_freeres (void); + __libc_freeres (); + } +#endif -/* patched memory functions */ + no_checking = 1; -/* force compiler to perform stores coded up to this point */ -#define barrier() __asm__ __volatile__ ("": : : "memory") + WAIT_SEM (); + while (alloca_list) { + alloca_list_type *next = alloca_list->next; -static void install_malloc_hooks(void) -{ -#ifdef CONFIG_TCC_MALLOC_HOOKS - saved_malloc_hook = __malloc_hook; - saved_free_hook = __free_hook; - saved_realloc_hook = __realloc_hook; - saved_memalign_hook = __memalign_hook; - __malloc_hook = __bound_malloc; - __free_hook = __bound_free; - __realloc_hook = __bound_realloc; - __memalign_hook = __bound_memalign; + 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; - barrier(); + 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 -} - -static void restore_malloc_hooks(void) -{ -#ifdef CONFIG_TCC_MALLOC_HOOKS - __malloc_hook = saved_malloc_hook; - __free_hook = saved_free_hook; - __realloc_hook = saved_realloc_hook; - __memalign_hook = saved_memalign_hook; - - barrier(); + POST_SEM (); + EXIT_SEM (); + 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_strchr_count %llu\n", bound_strchr_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 + } + } } -static void *libc_malloc(size_t size) -{ - void *ptr; - restore_malloc_hooks(); - ptr = malloc(size); - install_malloc_hooks(); - return ptr; -} - -static void libc_free(void *ptr) +#if HAVE_PTHREAD_CREATE +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) { - restore_malloc_hooks(); - free(ptr); - install_malloc_hooks(); + use_sem = 1; + dprintf (stderr, "%s, %s()\n", __FILE__, __FUNCTION__); + return pthread_create_redir(thread, attr, start_routine, arg); } +#endif -/* XXX: we should use a malloc which ensure that it is unlikely that - two malloc'ed data have the same address if 'free' are made in - between. */ +#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 = libc_malloc(size + 1); + ptr = BOUND_MALLOC (size + 1); + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); - if (!ptr) - return NULL; - - dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n", - __FILE__, __FUNCTION__, ptr, (unsigned)size); - - __bound_new_region(ptr, size); + if (no_checking == 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; - restore_malloc_hooks(); - -#ifndef HAVE_MEMALIGN +#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 = malloc(size + 1); + ptr = BOUND_MALLOC(size + 1); } -#else - /* 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 = memalign(size + 1, align); #endif - - install_malloc_hooks(); - - if (!ptr) - return NULL; + dprintf(stderr, "%s, %s(): %p, 0x%lx\n", + __FILE__, __FUNCTION__, ptr, (unsigned long)size); - dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n", - __FILE__, __FUNCTION__, ptr, (unsigned)size); + if (no_checking == 0) { + WAIT_SEM (); + INCR_COUNT(bound_memalign_count); - __bound_new_region(ptr, size); + 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 { - if (ptr == NULL) + 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; - if (__bound_delete_region(ptr) != 0) - bound_error("freeing invalid region"); - libc_free(ptr); + dprintf(stderr, "%s, %s(): %p\n", __FILE__, __FUNCTION__, ptr); + + if (no_checking == 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 *ptr1; - size_t old_size; + void *new_ptr; if (size == 0) { +#if MALLOC_REDIR + free(ptr); +#else __bound_free(ptr, caller); +#endif return NULL; - } else { - ptr1 = __bound_malloc(size, caller); - if (ptr == NULL || ptr1 == NULL) - return ptr1; - old_size = get_region_size(ptr); - if (old_size == EMPTY_SIZE) - bound_error("realloc'ing invalid pointer"); - memcpy(ptr1, ptr, old_size); - __bound_free(ptr, caller); - return ptr1; } + + 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 == 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; } -#ifndef CONFIG_TCC_MALLOC_HOOKS +#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 = size * nmemb; - ptr = __bound_malloc(size, NULL); - if (!ptr) - return NULL; - memset(ptr, 0, size); + + 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 == 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; } -#endif -#if 0 -static void bound_dump(void) -{ - BoundEntry *page, *e; - size_t i, j; - - fprintf(stderr, "region dump:\n"); - for(i=0;i<BOUND_T1_SIZE;i++) { - page = __bound_t1[i]; - for(j=0;j<BOUND_T2_SIZE;j++) { - e = page + j; - /* do not print invalid or empty entries */ - if (e->size != EMPTY_SIZE && e->start != 0) { - fprintf(stderr, "%08x:", - (i << (BOUND_T2_BITS + BOUND_T3_BITS)) + - (j << BOUND_T3_BITS)); - do { - fprintf(stderr, " %08lx:%08lx", e->start, e->start + e->size); - e = e->next; - } while (e != NULL); - fprintf(stderr, "\n"); - } - } +#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 == 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 == 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) +static void __bound_check(const void *p, size_t size, const char *function) { - if (size == 0) - return; - p = __bound_ptr_add((void *)p, size - 1); - if (p == INVALID_POINTER) - bound_error("invalid pointer"); + 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); + } } -void *__bound_memcpy(void *dst, const void *src, size_t size) +static int check_overlap (const void *p1, size_t n1, + const void *p2, size_t n2, + const char *function) { - void* p; + const void *p1e = (const void *) ((const char *) p1 + n1); + const void *p2e = (const void *) ((const char *) p2 + n2); + + if (no_checking == 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; +} - dprintf(stderr, "%s %s: start, dst=%p src=%p size=%x\n", - __FILE__, __FUNCTION__, dst, src, (unsigned)size); +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); +} - __bound_check(dst, size); - __bound_check(src, size); - /* check also region overlap */ - if (src >= dst && src < dst + size) - bound_error("overlapping regions in memcpy()"); +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; +} - p = memcpy(dst, src, size); +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); +} - dprintf(stderr, "%s %s: end, p=%p\n", __FILE__, __FUNCTION__, p); - return p; +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); } -void *__bound_memmove(void *dst, const void *src, size_t size) +#if defined(__arm__) +void *__bound___aeabi_memcpy(void *dest, const void *src, size_t n) { - __bound_check(dst, size); - __bound_check(src, size); - return memmove(dst, src, size); + 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_memset(void *dst, int c, size_t size) +void *__bound___aeabi_memmove(void *dest, const void *src, size_t n) { - __bound_check(dst, size); - return memset(dst, c, size); + 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); } -/* XXX: could be optimized */ +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; + 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; +} - len = 0; - for(;;) { - p = __bound_ptr_indir1((char *)s, len); - if (p == INVALID_POINTER) - bound_error("bad pointer in strlen()"); - if (*p == '\0') +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; - len++; + } + ++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_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; } - return len; + __bound_check(s, ((const char *)str - s) + 1, "strchr"); + return *str == ch ? (char *) str : NULL; } -char *__bound_strcpy(char *dst, const char *src) +char *__bound_strdup(const char *s) { - size_t len; - void *p; + 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 == 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; +} - dprintf(stderr, "%s %s: strcpy start, dst=%p src=%p\n", - __FILE__, __FUNCTION__, dst, src); - len = __bound_strlen(src); - p = __bound_memcpy(dst, src, len + 1); - dprintf(stderr, "%s %s: strcpy end, p = %p\n", - __FILE__, __FUNCTION__, p); - return p; +/* + 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/lib/bt-dll.c b/lib/bt-dll.c new file mode 100644 index 0000000..3389bd7 --- /dev/null +++ b/lib/bt-dll.c @@ -0,0 +1,73 @@ +/* ------------------------------------------------------------- */ +/* stubs for calling bcheck functions from a dll. */ + +#include <windows.h> +#include <stdio.h> + +#define REDIR_ALL \ + REDIR(__bt_init) \ + 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/lib/bt-exe.c b/lib/bt-exe.c new file mode 100644 index 0000000..a0e8fad --- /dev/null +++ b/lib/bt-exe.c @@ -0,0 +1,43 @@ +/* ------------------------------------------------------------- */ +/* for linking rt_printline and the signal/exception handler + from tccrun.c into executables. */ + +#define CONFIG_TCC_BACKTRACE_ONLY +#include "../tccrun.c" + +int (*__rt_error)(void*, void*, const char *, va_list); + +#ifndef _WIN32 +# define __declspec(n) +#endif + +__declspec(dllexport) +void __bt_init(rt_context *p, int num_callers, int mode) +{ + __attribute__((weak)) int main(); + __attribute__((weak)) void __bound_init(void*, int); + struct rt_context *rc = &g_rtctxt; + //fprintf(stderr, "__bt_init %d %p %p %d\n", num_callers, p->stab_sym, p->bounds_start, mode), fflush(stderr); + 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 (__bound_init && p->bounds_start) + __bound_init(p->bounds_start, mode); +} + +/* copy a string and truncate it. */ +static 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/lib/bt-log.c b/lib/bt-log.c new file mode 100644 index 0000000..d767f08 --- /dev/null +++ b/lib/bt-log.c @@ -0,0 +1,37 @@ +/* ------------------------------------------------------------- */ +/* 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 + +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; +} diff --git a/lib/dsohandle.c b/lib/dsohandle.c new file mode 100644 index 0000000..0993dbc --- /dev/null +++ b/lib/dsohandle.c @@ -0,0 +1 @@ +void * __dso_handle __attribute((visibility("hidden"))) = &__dso_handle; diff --git a/lib/fetch_and_add_arm.S b/lib/fetch_and_add_arm.S new file mode 100644 index 0000000..773128b --- /dev/null +++ b/lib/fetch_and_add_arm.S @@ -0,0 +1,28 @@ + .text + .align 2 + .global fetch_and_add_arm + .type fetch_and_add_arm, %function +fetch_and_add_arm: +#ifdef __TINYC__ + .int 0xee070fba + .int 0xe1903f9f + .int 0xe0833001 + .int 0xe1802f93 + .int 0xe3520000 + .int 0x1afffffa + .int 0xee070fba + .int 0xe12fff1e +#else + .arch armv6 + + 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 +#endif + .size fetch_and_add_arm, .-fetch_and_add_arm diff --git a/lib/fetch_and_add_arm64.S b/lib/fetch_and_add_arm64.S new file mode 100644 index 0000000..5795db5 --- /dev/null +++ b/lib/fetch_and_add_arm64.S @@ -0,0 +1,22 @@ + .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 diff --git a/lib/fetch_and_add_riscv64.S b/lib/fetch_and_add_riscv64.S new file mode 100644 index 0000000..b4cd575 --- /dev/null +++ b/lib/fetch_and_add_riscv64.S @@ -0,0 +1,16 @@ + .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 diff --git a/lib/lib-arm64.c b/lib/lib-arm64.c index b8fd9e8..33df509 100644 --- a/lib/lib-arm64.c +++ b/lib/lib-arm64.c @@ -24,10 +24,12 @@ void *memcpy(void*,void*,__SIZE_TYPE__); #include <string.h> #endif +#ifndef __riscv void __clear_cache(void *beg, void *end) { __arm64_clear_cache(beg, end); } +#endif typedef struct { uint64_t x0, x1; @@ -385,7 +387,12 @@ long double __extendsftf2(float f) else if (a << 1 >> 24 == 255) x.x1 = (0x7fff000000000000 | aa >> 31 << 63 | aa << 41 >> 16 | (uint64_t)!!(a << 9) << 47); - else + 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); @@ -404,7 +411,13 @@ long double __extenddftf2(double f) else if (a << 1 >> 53 == 2047) x.x1 = (0x7fff000000000000 | a >> 63 << 63 | a << 12 >> 16 | (uint64_t)!!(a << 12) << 47); - else + 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; diff --git a/lib/va_list.c b/lib/va_list.c index 8749f46..fab6675 100644 --- a/lib/va_list.c +++ b/lib/va_list.c @@ -3,7 +3,6 @@ #if defined __x86_64__ /* Avoid include files, they may not be available when cross compiling */ -extern void *memset(void *s, int c, __SIZE_TYPE__ n); extern void abort(void); /* This should be in sync with our include/stdarg.h */ @@ -12,6 +11,7 @@ enum __va_arg_type { }; /* GCC compatible definition of va_list. */ +/*predefined by TCC (tcc_predefs.h): typedef struct { unsigned int gp_offset; unsigned int fp_offset; @@ -20,23 +20,16 @@ typedef struct { char *overflow_arg_area; }; char *reg_save_area; -} __va_list_struct; +} __builtin_va_list[1]; +*/ -void __va_start(__va_list_struct *ap, void *fp) -{ - memset(ap, 0, sizeof(__va_list_struct)); - *ap = *(__va_list_struct *)((char *)fp - 16); - ap->overflow_arg_area = (char *)fp + ap->overflow_offset; - ap->reg_save_area = (char *)fp - 176 - 16; -} - -void *__va_arg(__va_list_struct *ap, - enum __va_arg_type arg_type, +void *__va_arg(__builtin_va_list ap, + int arg_type, int size, int align) { size = (size + 7) & ~7; align = (align + 7) & ~7; - switch (arg_type) { + switch ((enum __va_arg_type)arg_type) { case __va_gen_reg: if (ap->gp_offset + size <= 48) { ap->gp_offset += size; @@ -60,6 +53,7 @@ void *__va_arg(__va_list_struct *ap, default: /* should never happen */ abort(); + return 0; } } #endif |