summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile77
-rw-r--r--lib/alloca86-bt.S13
-rw-r--r--lib/alloca86.S9
-rw-r--r--lib/alloca86_64-bt.S47
-rw-r--r--lib/alloca86_64.S10
-rw-r--r--lib/armflush.c8
-rw-r--r--lib/bcheck.c2294
-rw-r--r--lib/bt-dll.c73
-rw-r--r--lib/bt-exe.c43
-rw-r--r--lib/bt-log.c37
-rw-r--r--lib/dsohandle.c1
-rw-r--r--lib/fetch_and_add_arm.S28
-rw-r--r--lib/fetch_and_add_arm64.S22
-rw-r--r--lib/fetch_and_add_riscv64.S16
-rw-r--r--lib/lib-arm64.c17
-rw-r--r--lib/va_list.c20
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