diff --git a/Makefile b/Makefile index 6c87f010..9e75beba 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ endif LIBTCC = libtcc.a LIBTCC1 = libtcc1.a +LIBTCCB1 = libtccb1.a LINK_LIBTCC = LIBS = CFLAGS += -I$(TOP) @@ -86,7 +87,7 @@ ifeq ($(INCLUDED),no) # running top Makefile PROGS = tcc$(EXESUF) -TCCLIBS = $(LIBTCC1) $(LIBTCC) $(LIBTCCDEF) +TCCLIBS = $(LIBTCC1) $(LIBTCCB1) $(LIBTCC) $(LIBTCCDEF) TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info all: $(PROGS) $(TCCLIBS) $(TCCDOCS) @@ -100,11 +101,15 @@ TCC_X += riscv64 LIBTCC1_X = i386 x86_64 i386-win32 x86_64-win32 x86_64-osx arm arm64 arm-wince LIBTCC1_X += riscv64 +# cross libtccb1.a targets to build +LIBTCCB1_X = i386 x86_64 i386-win32 x86_64-win32 + PROGS_CROSS = $(foreach X,$(TCC_X),$X-tcc$(EXESUF)) LIBTCC1_CROSS = $(foreach X,$(LIBTCC1_X),$X-libtcc1.a) +LIBTCCB1_CROSS = $(foreach X,$(LIBTCCB1_X),$X-libtcc1.a) # build cross compilers & libs -cross: $(LIBTCC1_CROSS) $(PROGS_CROSS) +cross: $(LIBTCC1_CROSS) $(LIBTCCB1_CROSS) $(PROGS_CROSS) # build specific cross compiler & lib cross-%: %-tcc$(EXESUF) %-libtcc1.a ; @@ -148,10 +153,10 @@ DEFINES += $(DEF-$(or $(findstring win,$T),unx)) ifneq ($(X),) ifeq ($(CONFIG_WIN32),yes) -DEF-win += -DTCC_LIBTCC1="\"$(X)libtcc1.a\"" -DEF-unx += -DTCC_LIBTCC1="\"lib/$(X)libtcc1.a\"" +DEF-win += -DTCC_LIBTCC1="\"$(X)libtcc1.a\"" -DTCC_LIBTCCB1="\"$(X)libtccb1.a\"" +DEF-unx += -DTCC_LIBTCC1="\"lib/$(X)libtcc1.a\"" -DTCC_LIBTCCB1="\"lib/$(X)libtccb1.a\"" else -DEF-all += -DTCC_LIBTCC1="\"$(X)libtcc1.a\"" +DEF-all += -DTCC_LIBTCC1="\"$(X)libtcc1.a\"" -DTCC_LIBTCCB1="\"$(X)libtccb1.a\"" DEF-win += -DCONFIG_TCCDIR="\"$(tccdir)/win32\"" endif endif @@ -269,6 +274,8 @@ STRIP_yes = -s LIBTCC1_W = $(filter %-win32-libtcc1.a %-wince-libtcc1.a,$(LIBTCC1_CROSS)) LIBTCC1_U = $(filter-out $(LIBTCC1_W),$(LIBTCC1_CROSS)) +LIBTCCB1_W = $(filter %-win32-libtccb1.a %-wince-libtccb1.a,$(LIBTCCB1_CROSS)) +LIBTCCB1_U = $(filter-out $(LIBTCCB1_W),$(LIBTCCB1_CROSS)) IB = $(if $1,mkdir -p $2 && $(INSTALLBIN) $1 $2) IBw = $(call IB,$(wildcard $1),$2) IF = $(if $1,mkdir -p $2 && $(INSTALL) $1 $2) @@ -279,6 +286,7 @@ IR = mkdir -p $2 && cp -r $1/. $2 install-unx: $(call IBw,$(PROGS) $(PROGS_CROSS),"$(bindir)") $(call IFw,$(LIBTCC1) $(LIBTCC1_U),"$(tccdir)") + $(call IFw,$(LIBTCCB1) $(LIBTCCB1_U),"$(tccdir)") $(call IF,$(TOPSRC)/include/*.h $(TOPSRC)/tcclib.h,"$(tccdir)/include") $(call $(if $(findstring .so,$(LIBTCC)),IBw,IFw),$(LIBTCC),"$(libdir)") $(call IF,$(TOPSRC)/libtcc.h,"$(includedir)") @@ -304,6 +312,7 @@ install-win: $(call IBw,$(PROGS) $(PROGS_CROSS) $(subst libtcc.a,,$(LIBTCC)),"$(bindir)") $(call IF,$(TOPSRC)/win32/lib/*.def,"$(tccdir)/lib") $(call IFw,libtcc1.a $(LIBTCC1_W),"$(tccdir)/lib") + $(call IFw,libtccb1.a $(LIBTCCB1_W),"$(tccdir)/lib") $(call IF,$(TOPSRC)/include/*.h $(TOPSRC)/tcclib.h,"$(tccdir)/include") $(call IR,$(TOPSRC)/win32/include,"$(tccdir)/include") $(call IR,$(TOPSRC)/win32/examples,"$(tccdir)/examples") diff --git a/arm-gen.c b/arm-gen.c index b93d298f..2b220e7c 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1264,8 +1264,9 @@ void gfunc_call(int nb_args) } /* generate function prolog of type 't' */ -void gfunc_prolog(CType *func_type) +void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; Sym *sym,*sym2; int n, nf, size, align, rs, struct_ret = 0; int addr, pn, sn; /* pn=core, sn=stack */ diff --git a/arm64-gen.c b/arm64-gen.c index 463541f1..121108e7 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -996,8 +996,9 @@ static int arm64_func_va_list_gr_offs; static int arm64_func_va_list_vr_offs; static int arm64_func_sub_sp_offset; -ST_FUNC void gfunc_prolog(CType *func_type) +ST_FUNC void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; int n = 0; int i = 0; Sym *sym; diff --git a/c67-gen.c b/c67-gen.c index 880a5720..c1e15acb 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -1939,8 +1939,9 @@ void gfunc_call(int nb_args) // parameters are loaded and restored upon return (or if/when needed). /* generate function prolog of type 't' */ -void gfunc_prolog(CType * func_type) +void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; int addr, align, size, func_call, i; Sym *sym; CType *type; diff --git a/i386-gen.c b/i386-gen.c index 51fbf073..214359ff 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -512,10 +512,12 @@ ST_FUNC void gfunc_call(int nb_args) #endif /* generate function prolog of type 't' */ -ST_FUNC void gfunc_prolog(CType *func_type) +ST_FUNC void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; int addr, align, size, func_call, fastcall_nb_regs; int param_index, param_addr; + int n_arg = 0; uint8_t *fastcall_regs_ptr; Sym *sym; CType *type; @@ -558,6 +560,7 @@ ST_FUNC void gfunc_prolog(CType *func_type) } /* define parameters */ while ((sym = sym->next) != NULL) { + n_arg++; type = &sym->type; size = type_size(type, &align); size = (size + 3) & ~3; @@ -597,6 +600,12 @@ ST_FUNC void gfunc_prolog(CType *func_type) func_bound_ind = ind; oad(0xb8, 0); /* lbound section pointer */ oad(0xb8, 0); /* call to function */ + if (n_arg >= 2 && strcmp (get_tok_str(func_sym->v, NULL), "main") == 0) { + o(0x0c458b); /* mov 0x12(%ebp),%eax */ + o(0x50); /* push %eax */ + gen_static_call(TOK___bound_main_arg); + o(0x04c483); /* add $0x4,%esp */ + } } #endif } @@ -1003,6 +1012,7 @@ ST_FUNC void gen_cvt_itof(int t) o(0x2404db); /* fildl (%esp) */ o(0x04c483); /* add $4, %esp */ } + vtop->r2 = VT_CONST; vtop->r = TREG_ST0; } @@ -1040,14 +1050,87 @@ ST_FUNC void ggoto(void) /* bound check support functions */ #ifdef CONFIG_TCC_BCHECK +ST_FUNC void tcc_add_bcheck(TCCState *s1) +{ + addr_t *ptr; + int loc_glob; + int sym_index; + int bsym_index; + + if (0 == s1->do_bounds_check) + return; + /* XXX: add an object file to do that */ + ptr = section_ptr_add(bounds_section, sizeof(*ptr)); + *ptr = 0; + loc_glob = s1->output_type != TCC_OUTPUT_MEMORY ? STB_LOCAL : STB_GLOBAL; + bsym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(loc_glob, STT_NOTYPE), 0, + bounds_section->sh_num, "__bounds_start"); + /* pull bcheck.o from libtcc1.a */ + sym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, "__bound_init"); + if (s1->output_type != TCC_OUTPUT_MEMORY) { + /* add 'call __bound_init()' in .init section */ + Section *init_section = find_section(s1, ".init"); + unsigned char *pinit; +#ifdef TCC_TARGET_PE + pinit = section_ptr_add(init_section, 3); + pinit[0] = 0x55; /* push %rbp */ + pinit[1] = 0x89; /* mov %esp,%ebp */ + pinit[2] = 0xe5; +#endif + pinit = section_ptr_add(init_section, 5); + pinit[0] = 0xe8; + write32le(pinit + 1, -4); + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 4, R_386_PC32, sym_index); + /* R_386_PC32 = R_X86_64_PC32 = 2 */ + pinit = section_ptr_add(init_section, 6); + pinit[0] = 0xb8; /* mov xx,%eax */ + write32le(pinit + 1, 0); + pinit[5] = 0x50; /* push %eax */ + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 5, R_386_32, bsym_index); + sym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, "__bounds_add_static_var"); + pinit = section_ptr_add(init_section, 5); + pinit[0] = 0xe8; + write32le(pinit + 1, -4); + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 4, R_386_PC32, sym_index); + /* R_386_PC32 = R_X86_64_PC32 = 2 */ + pinit = section_ptr_add(init_section, 3); + pinit[0] = 0x83; /* add $0x4,%esp */ + pinit[1] = 0xc4; + pinit[2] = 0x04; +#ifdef TCC_TARGET_PE + { + int init_index = set_elf_sym(symtab_section, + 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + init_section->sh_num, "__init_start"); + Sym sym; + init_section->sh_flags |= SHF_EXECINSTR; + pinit = section_ptr_add(init_section, 2); + pinit[0] = 0xc9; /* leave */ + pinit[1] = 0xc3; /* ret */ + sym.c = init_index; + add_init_array (s1, &sym); + } +#endif + } +} + /* generate a bounded pointer addition */ ST_FUNC void gen_bounded_ptr_add(void) { + /* save all temporary registers */ + save_regs(0); /* prepare fast i386 function call (args in eax and edx) */ gv2(RC_EAX, RC_EDX); - /* save all temporary registers */ vtop -= 2; - save_regs(0); /* do a fast function call */ gen_static_call(TOK___bound_ptr_add); /* returned pointer is in eax */ diff --git a/lib/Makefile b/lib/Makefile index 969d8e29..b8486075 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -8,6 +8,7 @@ VPATH = $(TOPSRC)/lib $(TOPSRC)/win32/lib T = $(or $(CROSS_TARGET),$(NATIVE_TARGET),unknown) X = $(if $(CROSS_TARGET),$(CROSS_TARGET)-) BIN = $(TOP)/$(X)libtcc1.a +BINB = $(TOP)/$(X)libtccb1.a XTCC ?= $(TOP)/$(X)tcc$(EXESUF) XCC = $(XTCC) @@ -19,6 +20,8 @@ XCFG = $(or $(findstring -win,$T),-unx) # in order to use gcc, tyoe: make -libtcc1-usegcc=yes arm-libtcc1-usegcc ?= no +x86_64-libtcc1-usegcc ?= no +i386-libtcc1-usegcc ?= no ifeq "$($(T)-libtcc1-usegcc)" "yes" XCC = $(CC) @@ -39,6 +42,8 @@ ifdef CONFIG_OSX XFLAGS += -D_ANSI_SOURCE endif +XFLAGS += -g -Wno-deprecated-declarations + 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 @@ -46,11 +51,11 @@ ARM64_O = lib-arm64.o RISCV64_O = lib-arm64.o WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o -OBJ-i386 = $(I386_O) $(BCHECK_O) $(DSO_O) -OBJ-x86_64 = $(X86_64_O) va_list.o $(BCHECK_O) $(DSO_O) +OBJ-i386 = $(I386_O) $(DSO_O) +OBJ-x86_64 = $(X86_64_O) va_list.o $(DSO_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-i386-win32 = $(I386_O) chkstk.o $(WIN_O) +OBJ-x86_64-win32 = $(X86_64_O) chkstk.o (WIN_O) OBJ-arm64 = $(ARM64_O) $(DSO_O) OBJ-arm = $(ARM_O) $(DSO_O) OBJ-arm-fpa = $(ARM_O) $(DSO_O) @@ -61,9 +66,29 @@ OBJ-arm-eabihf = $(ARM_O) $(DSO_O) OBJ-arm-wince = $(ARM_O) $(WIN_O) OBJ-riscv64 = $(RISCV64_O) $(DSO_O) +OBJB-i386 = $(BCHECK_O) +OBJB-x86_64 = $(BCHECK_O) +OBJB-x86_64-osx = +OBJB-i386-win32 = bcheck.o +OBJB-x86_64-win32 = bcheck.o +OBJB-arm64 = +OBJB-arm = +OBJB-arm-fpa = +OBJB-arm-fpa-ld = +OBJB-arm-vfp = +OBJB-arm-eabi = +OBJB-arm-eabihf = +OBJB-arm-wince = +OBJB-riscv64 = + +all: $(BIN) $(BINB) + $(BIN) : $(patsubst %.o,$(X)%.o,$(OBJ-$T)) $(XAR) rcs $@ $^ +$(BINB) : $(patsubst %.o,$(X)%.o,$(OBJB-$T)) + $(XAR) rcs $@ $^ + $(X)%.o : %.c $(XCC) -c $< -o $@ $(XFLAGS) @@ -74,4 +99,4 @@ $(X)crt1w.o : crt1.c $(X)wincrt1w.o : wincrt1.c clean : - rm -f *.a *.o $(BIN) + rm -f *.a *.o $(BIN) $(BINB) diff --git a/lib/bcheck.c b/lib/bcheck.c index 90f0ad2c..c9110bfe 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -34,20 +34,15 @@ #include #endif -/* #define BOUND_DEBUG */ +#define BOUND_DEBUG #ifdef BOUND_DEBUG - #define dprintf(a...) fprintf(a) + #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 */ - -/* use malloc hooks. Currently the code cannot be reliable if no hooks */ -#define CONFIG_TCC_MALLOC_HOOKS +/* Check memalign */ #define HAVE_MEMALIGN #if defined(__FreeBSD__) \ @@ -57,42 +52,58 @@ || 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 +#define INIT_SEM() +#define EXIT_SEM() +#define WAIT_SEM() +#define POST_SEM() +#define HAS_ENVIRON 0 +#define MALLOC_REDIR (0) +#else +#include +#include +#include +static sem_t bounds_sem; +#define INIT_SEM() sem_init (&bounds_sem, 0, 1) +#define EXIT_SEM() sem_destroy (&bounds_sem) +#define WAIT_SEM() while (sem_wait (&bounds_sem) < 0 && errno == EINTR); +#define POST_SEM() sem_post (&bounds_sem) +#define HAS_ENVIRON 0 /* Disabled for now */ +#define __USE_GNU /* get RTLD_NEXT */ +#include +#define MALLOC_REDIR (1) +static void *(*malloc_redir) (size_t) = NULL; +static void *(*calloc_redir) (size_t, size_t) = NULL; +static void (*free_redir) (void *) = NULL; +static void *(*realloc_redir) (void *, size_t) = NULL; +static void *(*memalign_redir) (size_t, size_t) = NULL; #endif -#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)) - -#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 BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS) -#define BOUND_T23_SIZE ((size_t)1 << BOUND_T23_BITS) - - /* 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; +}; + +typedef struct alloca_list_struct { + size_t fp; + void *p; + struct alloca_list_struct *next; +} alloca_list_type; + +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); #ifdef __attribute__ /* an __attribute__ macro is defined in the system headers */ @@ -100,64 +111,40 @@ int __bound_delete_region(void *p); #endif #define FASTCALL __attribute__((regparm(3))) +#if !MALLOC_REDIR 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); - -#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; +void *__bound_calloc(size_t nmemb, size_t size); #endif -/* TCC definitions */ -extern char __bounds_start; /* start of static bounds table */ +#define FREE_REUSE_SIZE (100) +static int free_reuse_index = 0; +static void *free_reuse_list[FREE_REUSE_SIZE]; + /* error message, just for TCC */ const char *__bound_error_msg; /* runtime error output */ extern void rt_error(size_t pc, const char *fmt, ...); -#ifdef BOUND_STATIC -static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */ -#else -static BoundEntry **__bound_t1; /* page table */ +static Tree *tree = NULL; +#define TREE_REUSE (1) +#if TREE_REUSE +static Tree *tree_free_list = NULL; #endif -static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */ -static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */ +static alloca_list_type *alloca_list = NULL; -static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) +static int inited = 0; +static int print_calls = 0; +static int never_fatal = 0; +static int no_checking = 0; + +/* enable/disable checking. This can be used for signal handlers. */ +void __bound_checking (int no_check) { - 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; + no_checking = no_check; } /* print a bound error message */ @@ -165,7 +152,8 @@ static void bound_error(const char *fmt, ...) { __bound_error_msg = fmt; fprintf(stderr,"%s %s: %s\n", __FILE__, __FUNCTION__, fmt); - *(void **)0 = 0; /* force a runtime error */ + if (never_fatal == 0) + *(void **)0 = 0; /* force a runtime error */ } static void bound_alloc_error(void) @@ -178,61 +166,74 @@ static void bound_alloc_error(void) void * FASTCALL __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); - - __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; + if (no_checking) { + return p + offset; } - 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 */ + + dprintf(stderr, "%s %s: %p 0x%x\n", + __FILE__, __FUNCTION__, p, (unsigned)offset); + + WAIT_SEM (); + if (tree) { + 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) { + addr += offset; + if (tree->is_invalid || addr >= tree->size) { + fprintf(stderr,"%s %s: %p is outside of the region\n", + __FILE__, __FUNCTION__, p + offset); + if (never_fatal == 0) { + POST_SEM (); + return INVALID_POINTER; /* return an invalid pointer */ + } + } + } } + 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 * FASTCALL __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%x start\n", \ + __FILE__, __FUNCTION__, p, (unsigned)offset); \ + WAIT_SEM (); \ + if (tree) { \ + 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) { \ + addr += offset + dsize; \ + if (tree->is_invalid || addr > tree->size) { \ + fprintf(stderr,"%s %s: %p is outside of the region\n", \ + __FILE__, __FUNCTION__, p + offset); \ + if (never_fatal == 0) { \ + POST_SEM (); \ + return INVALID_POINTER; /* return an invalid pointer */ \ + } \ + } \ + } \ + } \ + POST_SEM (); \ + return p + offset; \ } BOUND_PTR_INDIR(1) @@ -262,547 +263,258 @@ void FASTCALL __bound_local_new(void *p1) { size_t addr, size, fp, *p = p1; - dprintf(stderr, "%s, %s start p1=%p\n", __FILE__, __FUNCTION__, p); + if (no_checking) + return; GET_CALLER_FP(fp); + dprintf(stderr, "%s, %s local new p1=%p fp=%p\n", + __FILE__, __FUNCTION__, p, (void *)fp); + WAIT_SEM (); for(;;) { addr = p[0]; if (addr == 0) break; - addr += fp; - size = p[1]; + if (addr == 1) { + dprintf(stderr, "%s, %s() alloca/vla used\n", + __FILE__, __FUNCTION__); + } + else { + addr += fp; + size = p[1]; + dprintf(stderr, "%s, %s() (%p 0x%lx)\n", + __FILE__, __FUNCTION__, addr, (unsigned long) size); + } p += 2; - __bound_new_region((void *)addr, size); + tree = splay_insert(addr, size, tree); } - dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__); + POST_SEM (); } /* 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); + dprintf(stderr, "%s, %s local delete p1=%p fp=%p\n", + __FILE__, __FUNCTION__, p, (void *)fp); + WAIT_SEM (); for(;;) { addr = p[0]; if (addr == 0) break; - addr += fp; + if (addr == 1) { + while (alloca_list && alloca_list->fp == fp) { + dprintf(stderr, "%s, %s() remove alloca/vla %p\n", + __FILE__, __FUNCTION__, alloca_list->p); + alloca_list_type *next = alloca_list->next; + tree = splay_delete ((size_t) alloca_list->p, tree); +#if MALLOC_REDIR + free_redir (alloca_list); +#else + free (alloca_list); +#endif + alloca_list = next; + } + } + else { + addr += fp; + dprintf(stderr, "%s, %s() (%p 0x%lx)\n", + __FILE__, __FUNCTION__, (void *) addr, (unsigned long) p[1]); + } p += 2; - __bound_delete_region((void *)addr); + tree = splay_delete(addr, tree); } + POST_SEM (); } #if defined(__GNUC__) && (__GNUC__ >= 6) #pragma GCC diagnostic pop #endif -static BoundEntry *__bound_new_page(void) -{ - BoundEntry *page; - size_t i; - - page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); - if (!page) - bound_alloc_error(); - for(i=0;i> 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; - } - } 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; - } - } - 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; - } - } - } -} - void __bound_init(void) { - size_t i; - BoundEntry *page; - size_t start, size; - size_t *p; - - static int inited; if (inited) - return; + return; inited = 1; + print_calls = getenv ("TCC_BOUNDS_PRINT_CALLS") != NULL; + never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL; + dprintf(stderr, "%s, %s() start\n", __FILE__, __FUNCTION__); + INIT_SEM (); + +#if MALLOC_REDIR + { + void *addr = RTLD_NEXT; + + /* tcc -run required RTLD_DEFAULT. Normal usage requires RTLD_NEXT */ + *(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); + } +#endif + + tree = NULL; + /* 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= 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; - } + memset (free_reuse_list, 0, sizeof (free_reuse_list)); dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__); } -void __bound_main_arg(void **p) +void __bounds_add_static_var (size_t *p) { - void *start = p; - while (*p++); + dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); + /* add all static bound check values */ + WAIT_SEM (); + while (p[0] != 0) { + dprintf(stderr, "%s, %s() (%p 0x%lx)\n", + __FILE__, __FUNCTION__, (void *) p[0], (unsigned long) p[1]); + tree = splay_insert(p[0], p[1], tree); + p += 2; + } + POST_SEM (); +} - dprintf(stderr, "%s, %s calling __bound_new_region(%p %x)\n", - __FILE__, __FUNCTION__, start, (unsigned)((void *)p - start)); +void __bound_main_arg(char **p) +{ + char *start = (char *) p; - __bound_new_region(start, (void *) p - start); + WAIT_SEM (); + while (*p) { + dprintf(stderr, "%s, %s() (%p 0x%lx)\n", + __FILE__, __FUNCTION__, *p, (unsigned long)(strlen (*p) + 1)); + tree = splay_insert((size_t) *p, strlen (*p) + 1, tree); + p++; + } + dprintf(stderr, "%s, %s() argv (%p 0x%lx)\n", + __FILE__, __FUNCTION__, start, (unsigned long)((char *) p - start)); + tree = splay_insert((size_t) start, (char *) p - start, tree); + +#if HAS_ENVIRON + { + extern char **environ; + + p = environ; + start = (char *) p; + while (*p) { + dprintf(stderr, "%s, %s() (%p 0x%lx)\n", + __FILE__, __FUNCTION__, *p, (unsigned long)(strlen (*p) + 1)); + tree = splay_insert((size_t) *p, strlen (*p) + 1, tree); + p++; + } + dprintf(stderr, "%s, %s() environ(%p 0x%lx)\n", + __FILE__, __FUNCTION__, start, (unsigned long)((char *) p - start)); + tree = splay_insert((size_t) start, (char *) p - start, tree); + } +#endif + POST_SEM (); } void __bound_exit(void) { + int i; + dprintf(stderr, "%s, %s()\n", __FILE__, __FUNCTION__); - restore_malloc_hooks(); -} - -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; + while (tree) { + tree = splay_delete (tree->start, tree); } -} - -/* create a new region. It should not already exist in the region list */ -void __bound_new_region(void *p, size_t size) -{ - 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); - - - e = (BoundEntry *)((char *)page + t2_start); - add_region(e, start, size); - - if (t1_end == t1_start) { - /* same ending page */ - e2 = (BoundEntry *)((char *)page + t2_end); - if (e2 > e) { - e++; - for(;estart = start; - e->size = size; - } - add_region(e, start, size); - } - } else { - /* mark until end of page */ - e2 = page + BOUND_T2_SIZE; - e++; - for(;estart = start; - e->size = size; - } - /* mark intermediate pages, if any */ - for(i=t1_start+1;istart = start; - e->size = size; - } - } - /* last page */ - page = get_page(t1_end); - e2 = (BoundEntry *)((char *)page + t2_end); - for(e=page;estart = start; - e->size = size; - } - add_region(e, start, size); - } - - dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__); -} - -/* delete a region */ -static inline void delete_region(BoundEntry *e, void *p, size_t empty_size) -{ - size_t addr; - BoundEntry *e1; - - 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; - } - } - } -} - -/* WARNING: 'p' must be the starting point of the region. */ -/* return non zero if error */ -int __bound_delete_region(void *p) -{ - 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(); - - 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(;estart = 0; - e->size = empty_size; - } - delete_region(e, p, empty_size); - } - } else { - /* mark until end of page */ - e2 = page + BOUND_T2_SIZE; - e++; - for(;estart = 0; - e->size = empty_size; - } - /* mark intermediate pages, if any */ - /* XXX: should free them */ - for(i=t1_start+1;istart = 0; - e->size = empty_size; - } - } - /* last page */ - page = get_page(t1_end); - e2 = (BoundEntry *)((char *)page + t2_end); - for(e=page;estart = 0; - e->size = empty_size; - } - delete_region(e, p, empty_size); - } - - 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) -{ - 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; -} - -/* patched memory functions */ - -/* force compiler to perform stores coded up to this point */ -#define barrier() __asm__ __volatile__ ("": : : "memory") - -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; - - barrier(); +#if TREE_REUSE + while (tree_free_list) { + Tree *next = tree_free_list->left; +#if MALLOC_REDIR + free_redir (tree_free_list); +#else + free (tree_free_list); #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(); + tree_free_list = next; + } #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) -{ - restore_malloc_hooks(); - free(ptr); - install_malloc_hooks(); + for (i = 0; i < FREE_REUSE_SIZE; i++) { +#if MALLOC_REDIR + free_redir (free_reuse_list[i]); +#else + free (free_reuse_list[i]); +#endif + } + EXIT_SEM (); + inited = 0; } /* 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) { + static int pool_index = 0; + static unsigned char pool[256]; + void *retval; + + retval = &pool[pool_index]; + pool_index = (pool_index + size + 7) & ~8; + dprintf (stderr, "%s, %s initial (%p, 0x%x)\n", + __FILE__, __FUNCTION__, retval, (unsigned)size); + return retval; + } +#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); + WAIT_SEM (); +#if MALLOC_REDIR + ptr = malloc_redir (size); +#else + ptr = malloc(size + 1); +#endif - if (!ptr) - return NULL; + dprintf(stderr, "%s, %s (%p, 0x%x)\n", + __FILE__, __FUNCTION__, ptr, (unsigned)size); - dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n", - __FILE__, __FUNCTION__, ptr, (unsigned)size); - - __bound_new_region(ptr, size); + if (ptr) { + tree = splay_insert ((size_t) ptr, size, tree); + } + 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(); + WAIT_SEM (); #ifndef HAVE_MEMALIGN if (align > 4) { @@ -810,103 +522,229 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) ptr = NULL; } else { /* we suppose that malloc aligns to at least four bytes */ +#if MALLOC_REDIR + ptr = malloc_redir(size + 1); +#else ptr = malloc(size + 1); +#endif } #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 */ +#if MALLOC_REDIR + ptr = memalign_redir(size + 1, align); +#else ptr = memalign(size + 1, align); #endif - - install_malloc_hooks(); - - 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); +#endif + if (ptr) { + dprintf(stderr, "%s, %s (%p, 0x%x)\n", + __FILE__, __FUNCTION__, ptr, (unsigned)size); + tree = splay_insert((size_t) ptr, size, tree); + } + POST_SEM (); return ptr; } +#if MALLOC_REDIR +void free(void *ptr) +#else void __bound_free(void *ptr, const void *caller) +#endif { - if (ptr == NULL) - return; - if (__bound_delete_region(ptr) != 0) - bound_error("freeing invalid region"); + size_t addr = (size_t) ptr; + void *p; - libc_free(ptr); + if (ptr == NULL || tree == NULL) + return; + + dprintf(stderr, "%s, %s (%p)\n", __FILE__, __FUNCTION__, ptr); + + WAIT_SEM (); + tree = splay (addr, tree); + if (tree->start == addr) { + if (tree->is_invalid) { + bound_error("freeing invalid region"); + POST_SEM (); + 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; + } +#if MALLOC_REDIR + free_redir (ptr); +#else + free(ptr); +#endif + POST_SEM (); } +#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; + size_t last_size; 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; - } -} - -#ifndef CONFIG_TCC_MALLOC_HOOKS -void *__bound_calloc(size_t nmemb, size_t size) -{ - void *ptr; - size = size * nmemb; - ptr = __bound_malloc(size, NULL); - if (!ptr) - return NULL; - memset(ptr, 0, size); - return ptr; -} + WAIT_SEM (); + tree = splay ((size_t) ptr, tree); + if (tree->start != (size_t) ptr) { +#if MALLOC_REDIR + ptr = realloc_redir (ptr, size); +#else + ptr = realloc (ptr, size); #endif - -#if 0 -static void bound_dump(void) -{ - BoundEntry *page, *e; - size_t i, j; - - fprintf(stderr, "region dump:\n"); - for(i=0;isize != 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 (ptr) { + tree = splay_insert ((size_t) ptr, size, tree); } + POST_SEM (); + return ptr; + } + else { + last_size = tree->size; + POST_SEM (); +#if MALLOC_REDIR + ptr1 = malloc(size); +#else + ptr1 = __bound_malloc(size, caller); +#endif + if (ptr == NULL || ptr1 == NULL) + return ptr1; + memcpy(ptr1, ptr, last_size < size ? last_size : size); +#if MALLOC_REDIR + free(ptr); +#else + __bound_free(ptr, caller); +#endif + return ptr1; } } } + +#if MALLOC_REDIR +void *calloc(size_t nmemb, size_t size) +#else +void *__bound_calloc(size_t nmemb, size_t size) #endif +{ + void *ptr; + + size *= nmemb; +#if MALLOC_REDIR + ptr = malloc(size); +#else + ptr = __bound_malloc(size, NULL); +#endif + if (ptr) { + memset (ptr, 0, size); + } + return ptr; +} + +#if !defined(_WIN32) +void *__bound_mmap (void *start, size_t size, int prot, + int flags, int fd, off_t offset) +{ + void *result; + + dprintf(stderr, "%s, %s (%p, 0x%x)\n", + __FILE__, __FUNCTION__, start, (unsigned)size); + result = mmap (start, size, prot, flags, fd, offset); + if (result) { + WAIT_SEM (); + 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%x)\n", + __FILE__, __FUNCTION__, start, (unsigned)size); + WAIT_SEM (); + tree = splay_delete ((size_t) start, tree); + POST_SEM (); + result = munmap (start, size); + return result; +} +#endif + +/* used by alloca */ +void __bound_new_region(void *p, size_t size) +{ + size_t fp; + alloca_list_type *last; + alloca_list_type *cur; + + dprintf(stderr, "%s, %s (%p, 0x%x)\n", + __FILE__, __FUNCTION__, p, (unsigned)size); + WAIT_SEM (); + last = NULL; + cur = alloca_list; + while (cur && cur->fp == fp) { + if (cur->p == p) { + dprintf(stderr, "%s, %s() remove alloca/vla %p\n", + __FILE__, __FUNCTION__, alloca_list->p); + if (last) { + last->next = cur->next; + } + else { + alloca_list->next = cur->next; + } + tree = splay_delete((size_t)p, tree); +#if MALLOC_REDIR + free_redir (cur); +#else + free (cur); +#endif + } + last = cur; + cur = cur->next; + } + tree = splay_insert((size_t)p, size, tree); +#if MALLOC_REDIR + cur = malloc_redir (sizeof (alloca_list_type)); +#else + cur = malloc (sizeof (alloca_list_type)); +#endif + if (cur) { + GET_CALLER_FP (fp); + cur->fp = fp; + cur->p = p; + cur->next = alloca_list; + alloca_list = cur; + } + POST_SEM (); +} /* 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) { + if (no_checking) + return; if (size == 0) return; p = __bound_ptr_add((void *)p, size - 1); @@ -918,18 +756,14 @@ void *__bound_memcpy(void *dst, const void *src, size_t size) { void* p; - dprintf(stderr, "%s %s: start, dst=%p src=%p size=%x\n", - __FILE__, __FUNCTION__, dst, src, (unsigned)size); - __bound_check(dst, size); __bound_check(src, size); /* check also region overlap */ - if (src >= dst && src < dst + size) + if (no_checking == 0 && src >= dst && src < dst + size) bound_error("overlapping regions in memcpy()"); p = memcpy(dst, src, size); - dprintf(stderr, "%s %s: end, p=%p\n", __FILE__, __FUNCTION__, p); return p; } @@ -969,11 +803,253 @@ char *__bound_strcpy(char *dst, const char *src) size_t len; void *p; - 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 , 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) + +/* This is the comparison. */ +/* Returns <0 if i0 if i>j */ + +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; + + 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; + + 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; + + 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 + { +#if MALLOC_REDIR + new = (Tree *) malloc_redir (sizeof (Tree)); +#else + new = (Tree *) malloc (sizeof (Tree)); +#endif + } + if (new == NULL) {bound_alloc_error();} + 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->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; + + 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 +#if MALLOC_REDIR + free_redir(t); +#else + free(t); +#endif +#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; istart, (unsigned long) t->size, (unsigned)t->is_invalid); + splay_printtree(t->left, d+1); +} diff --git a/riscv64-gen.c b/riscv64-gen.c index 163657c8..2359a0eb 100644 --- a/riscv64-gen.c +++ b/riscv64-gen.c @@ -614,8 +614,9 @@ ST_FUNC void gfunc_call(int nb_args) static int func_sub_sp_offset, num_va_regs, func_va_list_ofs; -ST_FUNC void gfunc_prolog(CType *func_type) +ST_FUNC void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; int i, addr, align, size; int param_addr = 0; int areg[2]; diff --git a/tcc.h b/tcc.h index b6a46069..2c3f668f 100644 --- a/tcc.h +++ b/tcc.h @@ -292,6 +292,9 @@ extern long double strtold (const char *__nptr, char **__endptr); #ifndef TCC_LIBTCC1 # define TCC_LIBTCC1 "libtcc1.a" #endif +#ifndef TCC_LIBTCCB1 +# define TCC_LIBTCCB1 "libtccb1.a" +#endif /* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */ #if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC @@ -1202,6 +1205,9 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) ST_FUNC int tcc_add_crt(TCCState *s, const char *filename); #endif ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags); +#ifdef CONFIG_TCC_BCHECK +ST_FUNC void tcc_add_bcheck(TCCState *s1); +#endif ST_FUNC void tcc_add_pragma_libs(TCCState *s1); PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f); PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time); @@ -1518,7 +1524,7 @@ ST_FUNC void load(int r, SValue *sv); ST_FUNC void store(int r, SValue *v); ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize); ST_FUNC void gfunc_call(int nb_args); -ST_FUNC void gfunc_prolog(CType *func_type); +ST_FUNC void gfunc_prolog(Sym *func_sym); ST_FUNC void gfunc_epilog(void); ST_FUNC void gen_fill_nops(int); ST_FUNC int gjmp(int t); diff --git a/tccelf.c b/tccelf.c index 70e4f874..0fc85d1c 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1317,42 +1317,13 @@ ST_FUNC void add_fini_array (TCCState *s1, Sym *sym) add_array (".fini_array", s1, sym); } -ST_FUNC void tcc_add_bcheck(TCCState *s1) -{ -#ifdef CONFIG_TCC_BCHECK - addr_t *ptr; - int sym_index; - - if (0 == s1->do_bounds_check) - return; - /* XXX: add an object file to do that */ - ptr = section_ptr_add(bounds_section, sizeof(*ptr)); - *ptr = 0; - set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - bounds_section->sh_num, "__bounds_start"); - /* pull bcheck.o from libtcc1.a */ - sym_index = set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_UNDEF, "__bound_init"); - if (s1->output_type != TCC_OUTPUT_MEMORY) { - /* add 'call __bound_init()' in .init section */ - Section *init_section = find_section(s1, ".init"); - unsigned char *pinit = section_ptr_add(init_section, 5); - pinit[0] = 0xe8; - write32le(pinit + 1, -4); - put_elf_reloc(symtab_section, init_section, - init_section->data_offset - 4, R_386_PC32, sym_index); - /* R_386_PC32 = R_X86_64_PC32 = 2 */ - } -#endif -} - /* add tcc runtime libraries */ ST_FUNC void tcc_add_runtime(TCCState *s1) { s1->filetype = 0; +#ifdef CONFIG_TCC_BCHECK tcc_add_bcheck(s1); +#endif tcc_add_pragma_libs(s1); #ifndef TCC_TARGET_PE /* add libc */ @@ -1365,6 +1336,13 @@ ST_FUNC void tcc_add_runtime(TCCState *s1) else tcc_add_dll(s1, TCC_LIBGCC, 0); } +#endif +#ifdef CONFIG_TCC_BCHECK + if (s1->do_bounds_check && s1->output_type != TCC_OUTPUT_DLL) { + tcc_add_library_err(s1, "pthread"); + tcc_add_library_err(s1, "dl"); + tcc_add_support(s1, TCC_LIBTCCB1); + } #endif tcc_add_support(s1, TCC_LIBTCC1); /* add crt end if not memory output */ @@ -2814,6 +2792,7 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) const char *ar_names, *p; const uint8_t *ar_index; ElfW(Sym) *sym; + Section *s; data = tcc_malloc(size); if (full_read(fd, data, size) != size) @@ -2825,14 +2804,19 @@ static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize) do { bound = 0; for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) { - sym_index = find_elf_sym(symtab_section, p); + s = symtab_section; + sym_index = find_elf_sym(s, p); + if(sym_index == 0) { + s = s1->dynsymtab_section; + sym_index = find_elf_sym(s, p); + } if(sym_index) { - sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + sym = &((ElfW(Sym) *)s->data)[sym_index]; if(sym->st_shndx == SHN_UNDEF) { off = (entrysize == 4 - ? get_be32(ar_index + i * 4) - : get_be64(ar_index + i * 8)) - + sizeof(ArchiveHeader); + ? get_be32(ar_index + i * 4) + : get_be64(ar_index + i * 8)) + + sizeof(ArchiveHeader); ++bound; if(tcc_load_object_file(s1, fd, off) < 0) { fail: diff --git a/tccgen.c b/tccgen.c index a6181b0c..b41dd82c 100644 --- a/tccgen.c +++ b/tccgen.c @@ -405,6 +405,8 @@ ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, case TOK_strlen: case TOK_strcpy: case TOK_alloca: + case TOK_mmap: + case TOK_munmap: strcpy(buf, "__bound_"); strcat(buf, name); name = buf; @@ -1191,7 +1193,11 @@ ST_FUNC void save_reg_upstack(int r, int n) type = &int_type; #endif size = type_size(type, &align); - l=get_temp_local_var(size,align); + if ((p->r2 & VT_VALMASK) < VT_CONST) { + size *= 2; + align *= 2; + } + l=get_temp_local_var(size,align); sv.type.t = type->t; sv.r = VT_LOCAL | VT_LVAL; sv.c.i = l; @@ -1375,7 +1381,7 @@ static void gbound(void) vtop->r &= ~VT_MUSTBOUND; /* if lvalue, then use checking code before dereferencing */ - if (vtop->r & VT_LVAL) { + if ((vtop->r & VT_LVAL) && !nocode_wanted) { /* if not VT_BOUNDED value, then make one */ if (!(vtop->r & VT_BOUNDED)) { lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL); @@ -5478,6 +5484,15 @@ special_math_val: Sym *sa; int nb_args, ret_nregs, ret_align, regsize, variadic; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check && (vtop->r & VT_SYM) && vtop->sym->v == TOK_alloca) { + addr_t *bounds_ptr; + + bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t)); + bounds_ptr[0] = 1; /* marks alloca/vla used */ + bounds_ptr[1] = 0; + } +#endif /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { /* pointer test (no array accepted) */ @@ -7413,7 +7428,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, if ((r & VT_VALMASK) == VT_LOCAL) { sec = NULL; #ifdef CONFIG_TCC_BCHECK - if (bcheck && (type->t & VT_ARRAY)) { + if (bcheck && ((type->t & VT_ARRAY) || + (type->t & VT_BTYPE) == VT_STRUCT)) { loc--; } #endif @@ -7422,8 +7438,9 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, #ifdef CONFIG_TCC_BCHECK /* handles bounds */ /* XXX: currently, since we do only one pass, we cannot track - '&' operators, so we add only arrays */ - if (bcheck && (type->t & VT_ARRAY)) { + '&' operators, so we add only arrays/structs/unions */ + if (bcheck && ((type->t & VT_ARRAY) || + (type->t & VT_BTYPE) == VT_STRUCT)) { addr_t *bounds_ptr; /* add padding between regions */ loc--; @@ -7542,6 +7559,15 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, gen_vla_sp_save(addr); cur_scope->vla.loc = addr; cur_scope->vla.num++; +#ifdef CONFIG_TCC_BCHECK + if (bcheck) { + addr_t *bounds_ptr; + + bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t)); + bounds_ptr[0] = 1; /* marks alloca/vla used */ + bounds_ptr[1] = 0; + } +#endif } else if (has_init) { size_t oldreloc_offset = 0; @@ -7599,7 +7625,7 @@ static void gen_function(Sym *sym, AttributeDef *ad) /* push a dummy symbol to enable local sym storage */ sym_push2(&local_stack, SYM_FIELD, 0, 0); local_scope = 1; /* for function parameters */ - gfunc_prolog(&sym->type); + gfunc_prolog(sym); local_scope = 0; rsym = 0; clear_temp_local_var_list(); diff --git a/tccpe.c b/tccpe.c index a1f73b0d..6ecb87d9 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1880,15 +1880,29 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe) if (0 == s1->nostdlib) { static const char *libs[] = { - TCC_LIBTCC1, "msvcrt", "kernel32", "", "user32", "gdi32", NULL + TCC_LIBTCC1, +#ifdef CONFIG_TCC_BCHECK + TCC_LIBTCCB1, +#endif + "msvcrt", "kernel32", "", "user32", "gdi32", NULL }; const char **pp, *p; for (pp = libs; 0 != (p = *pp); ++pp) { +#ifdef CONFIG_TCC_BCHECK + if (pp == libs + 1 && + (s1->do_bounds_check == 0 || s1->output_type == TCC_OUTPUT_DLL)) { + continue; + } +#endif if (0 == *p) { if (PE_DLL != pe_type && PE_GUI != pe_type) break; } else if (pp == libs && tcc_add_dll(s1, p, 0) >= 0) { continue; +#ifdef CONFIG_TCC_BCHECK + } else if (pp == libs + 1 && tcc_add_dll(s1, p, 0) >= 0) { + continue; +#endif } else { tcc_add_library_err(s1, p); } diff --git a/tccrun.c b/tccrun.c index fe7a757b..ca87102f 100644 --- a/tccrun.c +++ b/tccrun.c @@ -137,30 +137,23 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) if (s1->do_bounds_check) { void (*bound_init)(void); void (*bound_exit)(void); - void (*bound_new_region)(void *p, addr_t size); - int (*bound_delete_region)(void *p); - int i, ret; + void (*bounds_add_static_var)(size_t *p); + size_t *bounds_start; + int ret; /* set error function */ rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg"); /* XXX: use .init section so that it also work in binary ? */ bound_init = tcc_get_symbol_err(s1, "__bound_init"); bound_exit = tcc_get_symbol_err(s1, "__bound_exit"); - bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region"); - bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region"); + bounds_add_static_var = tcc_get_symbol_err(s1, "__bounds_add_static_var"); + bounds_start = tcc_get_symbol_err(s1, "__bounds_start"); bound_init(); - /* mark argv area as valid */ - bound_new_region(argv, argc*sizeof(argv[0])); - for (i=0; im + 0) == 8 ? "%016lx\n" : "%02x\n", s->m) + +/* Test failed when using bounds checking */ +void bounds_check1_test (void) +{ + struct s { + int x; + long long y; + } _s, *s = &_s; + s->x = 10; + s->y = 20; + pv(x); + pv(y); +} diff --git a/tests/tests2/110_average.c b/tests/tests2/110_average.c new file mode 100644 index 00000000..273b5110 --- /dev/null +++ b/tests/tests2/110_average.c @@ -0,0 +1,27 @@ +#include + +typedef struct +{ + double average; + int count; +} +stats_type; + +static void +testc (stats_type *s, long long data) +{ + s->average = (s->average * s->count + data) / (s->count + 1); + s->count++; +} + +int main (void) +{ + stats_type s; + + s.average = 0; + s.count = 0; + testc (&s, 10); + testc (&s, 20); + printf ("%g %d\n", s.average, s.count); + return 0; +} diff --git a/tests/tests2/110_average.expect b/tests/tests2/110_average.expect new file mode 100644 index 00000000..4955335c --- /dev/null +++ b/tests/tests2/110_average.expect @@ -0,0 +1 @@ +15 2 diff --git a/tests/tests2/111_conversion.c b/tests/tests2/111_conversion.c new file mode 100644 index 00000000..c0815e1e --- /dev/null +++ b/tests/tests2/111_conversion.c @@ -0,0 +1,22 @@ +#include + +union u { + unsigned long ul; + long double ld; +}; + +void +conv (union u *p) +{ + p->ul = (unsigned int) p->ld; +} + +int main (void) +{ + union u v; + + v.ld = 42; + conv (&v); + printf ("%lu\n", v.ul); + return 0; +} diff --git a/tests/tests2/111_conversion.expect b/tests/tests2/111_conversion.expect new file mode 100644 index 00000000..d81cc071 --- /dev/null +++ b/tests/tests2/111_conversion.expect @@ -0,0 +1 @@ +42 diff --git a/x86_64-gen.c b/x86_64-gen.c index cc66b607..ce920d1b 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -141,6 +141,7 @@ ST_DATA const int reg_classes[NB_REGS] = { static unsigned long func_sub_sp_offset; static int func_ret_sub; +static int in_call; /* XXX: make it faster ? */ ST_FUNC void g(int c) @@ -636,16 +637,101 @@ static void gcall_or_jmp(int is_jmp) } #if defined(CONFIG_TCC_BCHECK) -#ifndef TCC_TARGET_PE static addr_t func_bound_offset; static unsigned long func_bound_ind; -#endif -static void gen_static_call(int v) +static void gen_bounds_call(int v) { Sym *sym = external_global_sym(v, &func_old_type); oad(0xe8, 0); +#ifdef TCC_TARGET_PE greloca(cur_text_section, sym, ind-4, R_X86_64_PC32, -4); +#else + greloca(cur_text_section, sym, ind-4, R_X86_64_PLT32, -4); +#endif +} + +ST_FUNC void tcc_add_bcheck(TCCState *s1) +{ + addr_t *ptr; + int loc_glob; + int sym_index; + int bsym_index; + + if (0 == s1->do_bounds_check) + return; + /* XXX: add an object file to do that */ + ptr = section_ptr_add(bounds_section, sizeof(*ptr)); + *ptr = 0; + loc_glob = s1->output_type != TCC_OUTPUT_MEMORY ? STB_LOCAL : STB_GLOBAL; + bsym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(loc_glob, STT_NOTYPE), 0, + bounds_section->sh_num, "__bounds_start"); + /* pull bcheck.o from libtcc1.a */ + sym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, "__bound_init"); + if (s1->output_type != TCC_OUTPUT_MEMORY) { + /* add 'call __bound_init()' in .init section */ + Section *init_section = find_section(s1, ".init"); + unsigned char *pinit; +#ifdef TCC_TARGET_PE + pinit = section_ptr_add(init_section, 8); + pinit[0] = 0x55; /* push %rbp */ + pinit[1] = 0x48; /* mov %rsp,%rpb */ + pinit[2] = 0x89; + pinit[3] = 0xe5; + pinit[4] = 0x48; /* sub $0x10,%rsp */ + pinit[5] = 0x83; + pinit[6] = 0xec; + pinit[7] = 0x10; +#endif + pinit = section_ptr_add(init_section, 5); + pinit[0] = 0xe8; + write32le(pinit + 1, -4); + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 4, R_386_PC32, sym_index); + /* R_386_PC32 = R_X86_64_PC32 = 2 */ + pinit = section_ptr_add(init_section, 13); + pinit[0] = 0x48; /* mov xx,%rax */ + pinit[1] = 0xb8; + write64le(pinit + 2, 0); +#ifdef TCC_TARGET_PE + pinit[10] = 0x48; /* mov %rax,%rcx */ + pinit[11] = 0x89; + pinit[12] = 0xc1; +#else + pinit[10] = 0x48; /* mov %rax,%rdi */ + pinit[11] = 0x89; + pinit[12] = 0xc7; +#endif + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 11, R_X86_64_64, bsym_index); + sym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, "__bounds_add_static_var"); + pinit = section_ptr_add(init_section, 5); + pinit[0] = 0xe8; + write32le(pinit + 1, -4); + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 4, R_386_PC32, sym_index); + /* R_386_PC32 = R_X86_64_PC32 = 2 */ +#ifdef TCC_TARGET_PE + { + int init_index = set_elf_sym(symtab_section, + 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + init_section->sh_num, "__init_start"); + Sym sym; + init_section->sh_flags |= SHF_EXECINSTR; + pinit = section_ptr_add(init_section, 2); + pinit[0] = 0xc9; /* leave */ + pinit[1] = 0xc3; /* ret */ + sym.c = init_index; + add_init_array (s1, &sym); + } +#endif + } } /* generate a bounded pointer addition */ @@ -654,22 +740,49 @@ ST_FUNC void gen_bounded_ptr_add(void) /* save all temporary registers */ save_regs(0); + if (in_call) { + o(0x51525657); /* push $rdi/%rsi/%rdx/%rcx */ + o(0x51415041); /* push $r8/%r9 */ + o(0x53415241); /* push $r10/%r11 */ + } + /* prepare fast x86_64 function call */ gv(RC_RAX); +#ifdef TCC_TARGET_PE + o(0xc28948); // mov %rax,%rdx ## second arg in %rdx, this must be size +#else o(0xc68948); // mov %rax,%rsi ## second arg in %rsi, this must be size +#endif vtop--; gv(RC_RAX); +#ifdef TCC_TARGET_PE + o(0xc18948); // mov %rax,%rcx ## first arg in %rcx, this must be ptr +#else o(0xc78948); // mov %rax,%rdi ## first arg in %rdi, this must be ptr +#endif vtop--; +#ifdef TCC_TARGET_PE + o(0x20ec8348); /* sub $20, %rsp */ +#endif + /* do a fast function call */ - gen_static_call(TOK___bound_ptr_add); + gen_bounds_call(TOK___bound_ptr_add); + +#ifdef TCC_TARGET_PE + o(0x20c48348); /* add $20, %rsp */ +#endif /* returned pointer is in rax */ vtop++; vtop->r = TREG_RAX | VT_BOUNDED; + if (in_call) { + o(0x5a415b41); /* pop $r11/%r10 */ + o(0x58415941); /* pop $r9/%r8 */ + o(0x5f5e5a59); /* pop $rcx/$rdx/$rsi/%rdi */ + } /* relocation offset of the bounding function call point */ vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela))); @@ -799,6 +912,8 @@ void gfunc_call(int nb_args) int size, r, args_size, i, d, bt, struct_size; int arg; + in_call = 1; + args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE; arg = nb_args; @@ -909,7 +1024,7 @@ void gfunc_call(int nb_args) if ((vtop->r & VT_SYM) && vtop->sym->v == TOK_alloca) { /* need to add the "func_scratch" area after alloca */ - o(0x48); func_alloca = oad(0x05, func_alloca); /* sub $NN, %rax */ + o(0x48); func_alloca = oad(0x2d, func_alloca); /* sub $NN, %rax */ } /* other compilers don't clear the upper bits when returning char/short */ @@ -929,17 +1044,20 @@ void gfunc_call(int nb_args) o(0xc089); /* mov %eax,%eax */ #endif vtop--; + in_call = 0; } #define FUNC_PROLOG_SIZE 11 /* generate function prolog of type 't' */ -void gfunc_prolog(CType *func_type) +void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; int addr, reg_param_index, bt, size; Sym *sym; CType *type; + int n_arg = 0; func_ret_sub = 0; func_scratch = 0; @@ -967,6 +1085,7 @@ void gfunc_prolog(CType *func_type) /* define parameters */ while ((sym = sym->next) != NULL) { + n_arg++; type = &sym->type; bt = type->t & VT_BTYPE; size = gfunc_arg_size(type); @@ -1002,6 +1121,25 @@ void gfunc_prolog(CType *func_type) } reg_param_index++; } +#ifdef CONFIG_TCC_BCHECK + /* leave some room for bound checking code */ + if (tcc_state->do_bounds_check) { + func_bound_offset = lbounds_section->data_offset; + func_bound_ind = ind; + o(0xb848); /* lbound section pointer */ + gen_le64 (0); + o(0xc18948); /* mov %rax,%rcx ## first arg in %rdi, this must be ptr */ + o(0x20ec8348); /* sub $20, %rsp */ + oad(0xb8, 0); /* call to function */ + o(0x20c48348); /* add $20, %rsp */ + if (n_arg >= 2 && strcmp (get_tok_str(func_sym->v, NULL), "main") == 0) { + o(0x184d8b48); /* mov 0x18(%rbp),%rcx */ + o(0x20ec8348); /* sub $20, %rsp */ + gen_bounds_call(TOK___bound_main_arg); + o(0x20c48348); /* add $20, %rsp */ + } + } +#endif } /* generate function epilog */ @@ -1009,6 +1147,40 @@ void gfunc_epilog(void) { int v, saved_ind; +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check + && func_bound_offset != lbounds_section->data_offset) + { + addr_t saved_ind; + addr_t *bounds_ptr; + Sym *sym_data; + + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t)); + *bounds_ptr = 0; + + /* generate bound local allocation */ + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, lbounds_section->data_offset); + saved_ind = ind; + ind = func_bound_ind; + greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0); + ind = ind + 10 + 3 + 4; + gen_bounds_call(TOK___bound_local_new); + ind = saved_ind; + + /* generate bound check local freeing */ + o(0x5250); /* save returned value, if any */ + greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0); + o(0xb848); /* mov xxx, %rax */ + gen_le64 (0); + o(0xc18948); /* mov %rax,%rcx # first arg in %rdi, this must be ptr */ + o(0x20ec8348); /* sub $20, %rsp */ + gen_bounds_call(TOK___bound_local_delete); + o(0x20c48348); /* add $20, %rsp */ + o(0x585a); /* restore returned value, if any */ + } +#endif o(0xc9); /* leave */ if (func_ret_sub == 0) { o(0xc3); /* ret */ @@ -1226,6 +1398,8 @@ void gfunc_call(int nb_args) int sse_reg, gen_reg; char _onstack[nb_args ? nb_args : 1], *onstack = _onstack; + in_call = 1; + /* calculate the number of integer/float register arguments, remember arguments to be passed via stack (in onstack[]), and also remember if we have to align the stack pointer to 16 (onstack[i] == 2). Needs @@ -1419,6 +1593,7 @@ void gfunc_call(int nb_args) else if (bt == (VT_SHORT | VT_UNSIGNED)) o(0xc0b70f); /* movzwl %al, %eax */ vtop--; + in_call = 0; } @@ -1430,11 +1605,13 @@ static void push_arg_reg(int i) { } /* generate function prolog of type 't' */ -void gfunc_prolog(CType *func_type) +void gfunc_prolog(Sym *func_sym) { + CType *func_type = &func_sym->type; X86_64_Mode mode; int i, addr, align, size, reg_count; int param_addr = 0, reg_param_index, sse_param_index; + int n_arg = 0; Sym *sym; CType *type; @@ -1518,6 +1695,7 @@ void gfunc_prolog(CType *func_type) } /* define parameters */ while ((sym = sym->next) != NULL) { + n_arg++; type = &sym->type; mode = classify_x86_64_arg(type, NULL, &size, &align, ®_count); switch (mode) { @@ -1574,9 +1752,14 @@ void gfunc_prolog(CType *func_type) if (tcc_state->do_bounds_check) { func_bound_offset = lbounds_section->data_offset; func_bound_ind = ind; - oad(0xb8, 0); /* lbound section pointer */ - o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */ - oad(0xb8, 0); /* call to function */ + o(0xb848); /* lbound section pointer */ + gen_le64 (0); + o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */ + oad(0xb8, 0); /* call to function */ + if (n_arg >= 2 && strcmp (get_tok_str(func_sym->v, NULL), "main") == 0) { + o(0xf07d8b48); /* mov -0x10(%rbp),%rdi */ + gen_bounds_call(TOK___bound_main_arg); + } } #endif } @@ -1588,7 +1771,7 @@ void gfunc_epilog(void) #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check - && func_bound_offset != lbounds_section->data_offset) + && func_bound_offset != lbounds_section->data_offset) { addr_t saved_ind; addr_t *bounds_ptr; @@ -1603,17 +1786,18 @@ void gfunc_epilog(void) func_bound_offset, lbounds_section->data_offset); saved_ind = ind; ind = func_bound_ind; - greloca(cur_text_section, sym_data, ind + 1, R_X86_64_64, 0); - ind = ind + 5 + 3; - gen_static_call(TOK___bound_local_new); + greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0); + ind = ind + 10 + 3; + gen_bounds_call(TOK___bound_local_new); ind = saved_ind; /* generate bound check local freeing */ o(0x5250); /* save returned value, if any */ - greloca(cur_text_section, sym_data, ind + 1, R_X86_64_64, 0); - oad(0xb8, 0); /* mov xxx, %rax */ + greloca(cur_text_section, sym_data, ind + 2, R_X86_64_64, 0); + o(0xb848); /* mov xxx, %rax */ + gen_le64 (0); o(0xc78948); /* mov %rax,%rdi # first arg in %rdi, this must be ptr */ - gen_static_call(TOK___bound_local_delete); + gen_bounds_call(TOK___bound_local_delete); o(0x585a); /* restore returned value, if any */ } #endif @@ -1940,6 +2124,7 @@ void gen_opf(int op) v1.c.i = fc; load(r, &v1); fc = 0; + vtop->r = r = r | VT_LVAL; } if (op == TOK_EQ || op == TOK_NE) { @@ -2007,6 +2192,7 @@ void gen_opf(int op) v1.c.i = fc; load(r, &v1); fc = 0; + vtop->r = r = r | VT_LVAL; } assert(!(vtop[-1].r & VT_LVAL));