diff --git a/arm-gen.c b/arm-gen.c index b93d298..2b220e7 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 463541f..121108e 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 880a572..c1e15ac 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 51fbf07..9b1fdd5 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; } diff --git a/lib/Makefile b/lib/Makefile index 969d8e2..1750fa1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -19,6 +19,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 +41,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 diff --git a/lib/bcheck.c b/lib/bcheck.c index 90f0ad2..6854b4e 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -28,16 +28,28 @@ && !defined(__OpenBSD__) \ && !defined(__NetBSD__) #include +#include +#include +static sem_t bounds_sem; +#define INIT_SEM() sem_init (&bounds_sem, 0, 1) +#define WAIT_SEM() while (sem_wait (&bounds_sem) < 0 && errno == EINTR); +#define POST_SEM() sem_post (&bounds_sem) +#define HAS_ENVIRON 1 +#else +#define INIT_SEM() +#define WAIT_SEM() +#define POST_SEM() +#define HAS_ENVIRON 0 #endif #if !defined(_WIN32) #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 @@ -65,7 +77,7 @@ #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_E_BITS (sizeof(size_t) == 4 ? 4 : 5) #define BOUND_T1_SIZE ((size_t)1 << BOUND_T1_BITS) #define BOUND_T2_SIZE ((size_t)1 << BOUND_T2_BITS) @@ -94,6 +106,9 @@ void __bound_init(void); void __bound_new_region(void *p, size_t size); int __bound_delete_region(void *p); +/* debug */ +void bound_dump(void); + #ifdef __attribute__ /* an __attribute__ macro is defined in the system headers */ #undef __attribute__ @@ -132,6 +147,9 @@ static BoundEntry **__bound_t1; /* page table */ static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */ static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */ +static int print_calls = 0; +static int never_fatal = 0; + static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) { size_t addr, tmp; @@ -141,7 +159,7 @@ static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) while (e != NULL) { addr = (size_t)p; addr -= e->start; - if (addr <= e->size) { + if (addr < e->size) { /* put region at the head */ tmp = e1->start; e1->start = e->start; @@ -149,6 +167,30 @@ static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) tmp = e1->size; e1->size = e->size; e->size = tmp; + tmp = e1->is_invalid; + e1->is_invalid = e->is_invalid; + e->is_invalid = 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; +} + +static BoundEntry *__bound_find_region_end(BoundEntry *e1, void *p) +{ + size_t addr; + BoundEntry *e; + + e = e1; + while (e != NULL) { + addr = (size_t)p; + addr -= e->start; + if (addr == e->size) { return e1; } e = e->next; @@ -165,7 +207,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) @@ -179,27 +222,41 @@ void * FASTCALL __bound_ptr_add(void *p, size_t offset) { size_t addr = (size_t)p; BoundEntry *e; + BoundEntry *s; - dprintf(stderr, "%s %s: %p %x\n", + dprintf(stderr, "%s %s: %p 0x%x\n", __FILE__, __FUNCTION__, p, (unsigned)offset); __bound_init(); + WAIT_SEM (); 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))); + s = e; addr -= e->start; - if (addr > e->size) { + 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 */ + if (e->size == EMPTY_SIZE || addr >= e->size) { + if (e->size == EMPTY_SIZE || e->is_invalid) { + e = __bound_find_region_end(s, p); + addr = (size_t)p - e->start; + addr += offset; + } + if (!e->is_invalid && addr >= e->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; } @@ -210,28 +267,42 @@ void * FASTCALL __bound_ptr_indir ## dsize (void *p, size_t offset) \ { \ size_t addr = (size_t)p; \ BoundEntry *e; \ + BoundEntry *s; \ \ - dprintf(stderr, "%s %s: %p %x start\n", \ - __FILE__, __FUNCTION__, p, (unsigned)offset); \ - \ - __bound_init(); \ + dprintf(stderr, "%s %s: %p 0x%x start\n", \ + __FILE__, __FUNCTION__, p, (unsigned)offset); \ + \ + __bound_init(); \ + WAIT_SEM (); \ 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))); \ + s = e; \ addr -= e->start; \ - if (addr > e->size) { \ + 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 */ \ - } \ + if (e->size == EMPTY_SIZE || addr > e->size) { \ + if (e->size == EMPTY_SIZE || e->is_invalid) { \ + e = __bound_find_region_end(s, p); \ + addr = (size_t)p - e->start; \ + addr += offset + dsize; \ + } \ + if (!e->is_invalid && addr > e->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 */ \ + } \ + } \ + } \ dprintf(stderr, "%s %s: return p+offset = %p\n", \ __FILE__, __FUNCTION__, p + offset); \ + POST_SEM (); \ return p + offset; \ } @@ -262,8 +333,8 @@ 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); GET_CALLER_FP(fp); + dprintf(stderr, "%s, %s local new p1=%p fp=%p\n", __FILE__, __FUNCTION__, p, (void *)fp); for(;;) { addr = p[0]; if (addr == 0) @@ -281,6 +352,7 @@ void FASTCALL __bound_local_delete(void *p1) { size_t addr, fp, *p = p1; GET_CALLER_FP(fp); + dprintf(stderr, "%s, %s local delete p1=%p fp=%p\n", __FILE__, __FUNCTION__, p, (void *)fp); for(;;) { addr = p[0]; if (addr == 0) @@ -300,7 +372,9 @@ static BoundEntry *__bound_new_page(void) BoundEntry *page; size_t i; - page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); + restore_malloc_hooks(); + page = malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); + install_malloc_hooks(); if (!page) bound_alloc_error(); for(i=0;i> BOUND_T2_BITS; @@ -397,16 +475,20 @@ 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 (); + /* save malloc hooks and install bound check hooks */ install_malloc_hooks(); @@ -467,25 +549,44 @@ void __bound_init(void) mark_invalid(start, size); #endif + dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__); +} + +void __bounds_add_static_var (size_t *p) +{ /* 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; } - - dprintf(stderr, "%s, %s() end\n\n", __FILE__, __FUNCTION__); } -void __bound_main_arg(void **p) +void __bound_main_arg(char **p) { void *start = p; - while (*p++); + while (*p) { + __bound_new_region(*p, strlen (*p) + 1); + p++; + } - dprintf(stderr, "%s, %s calling __bound_new_region(%p %x)\n", + dprintf(stderr, "%s, %s calling __bound_new_region(%p 0x%x)\n", __FILE__, __FUNCTION__, start, (unsigned)((void *)p - start)); __bound_new_region(start, (void *) p - start); + +#if HAS_ENVIRON + { + extern char **environ; + + p = environ; + start = p; + while (*p) { + __bound_new_region(*p, strlen (*p) + 1); + p++; + } + __bound_new_region(start, (void *) p - start); + } +#endif } void __bound_exit(void) @@ -502,15 +603,18 @@ static inline void add_region(BoundEntry *e, /* no region : add it */ e->start = start; e->size = size; + e->is_invalid = 0; } 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; + e1->is_invalid = e->is_invalid; e->start = start; e->size = size; e->next = e1; + e1->is_invalid = 0; } } @@ -521,13 +625,14 @@ void __bound_new_region(void *p, size_t size) BoundEntry *page, *e, *e2; size_t t1_start, t1_end, i, t2_start, t2_end; - dprintf(stderr, "%s, %s(%p, %x) start\n", + dprintf(stderr, "%s, %s(%p, 0x%x) start\n", __FILE__, __FUNCTION__, p, (unsigned)size); __bound_init(); + WAIT_SEM (); start = (size_t)p; - end = start + size; + end = start + size - 1; t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); @@ -579,6 +684,7 @@ void __bound_new_region(void *p, size_t size) } add_region(e, start, size); } + POST_SEM (); dprintf(stderr, "%s, %s end\n", __FILE__, __FUNCTION__); } @@ -591,18 +697,20 @@ static inline void delete_region(BoundEntry *e, void *p, size_t empty_size) addr = (size_t)p; addr -= e->start; - if (addr <= e->size) { + 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; + e->is_invalid = empty_size == INVALID_SIZE; } else { /* copy next region in head */ e->start = e1->start; e->size = e1->size; e->next = e1->next; + e->is_invalid = e1->is_invalid; bound_free_entry(e1); } } else { @@ -614,7 +722,7 @@ static inline void delete_region(BoundEntry *e, void *p, size_t empty_size) if (e == NULL) break; addr = (size_t)p - e->start; - if (addr <= e->size) { + if (addr < e->size) { /* found: remove entry */ e1->next = e->next; bound_free_entry(e); @@ -632,10 +740,11 @@ int __bound_delete_region(void *p) BoundEntry *page, *e, *e2; size_t t1_start, t1_end, t2_start, t2_end, i; - dprintf(stderr, "%s %s() start\n", __FILE__, __FUNCTION__); + dprintf(stderr, "%s %s(%p) start\n", __FILE__, __FUNCTION__, p); __bound_init(); + WAIT_SEM (); start = (size_t)p; t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & @@ -648,15 +757,18 @@ int __bound_delete_region(void *p) if (addr > e->size) e = __bound_find_region(e, p); /* test if invalid region */ - if (e->size == EMPTY_SIZE || (size_t)p != e->start) + if (e->size == EMPTY_SIZE || (size_t)p != e->start) { + POST_SEM (); 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; + end = start + size - 1; /* now we can free each entry */ t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); @@ -702,6 +814,7 @@ int __bound_delete_region(void *p) } delete_region(e, p, empty_size); } + POST_SEM (); dprintf(stderr, "%s %s() end\n", __FILE__, __FUNCTION__); @@ -713,8 +826,10 @@ int __bound_delete_region(void *p) static size_t get_region_size(void *p) { size_t addr = (size_t)p; + size_t size; BoundEntry *e; + WAIT_SEM (); e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; e = (BoundEntry *)((char *)e + ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & @@ -723,8 +838,11 @@ static size_t get_region_size(void *p) if (addr > e->size) e = __bound_find_region(e, p); if (e->start != (size_t)p) - return EMPTY_SIZE; - return e->size; + size = EMPTY_SIZE; + else + size = e->size; + POST_SEM (); + return size; } /* patched memory functions */ @@ -763,17 +881,21 @@ static void restore_malloc_hooks(void) static void *libc_malloc(size_t size) { void *ptr; + WAIT_SEM (); restore_malloc_hooks(); ptr = malloc(size); install_malloc_hooks(); + POST_SEM (); return ptr; } static void libc_free(void *ptr) { + WAIT_SEM (); restore_malloc_hooks(); free(ptr); install_malloc_hooks(); + POST_SEM (); } /* XXX: we should use a malloc which ensure that it is unlikely that @@ -791,7 +913,7 @@ void *__bound_malloc(size_t size, const void *caller) if (!ptr) return NULL; - dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n", + dprintf(stderr, "%s, %s calling __bound_new_region(%p, 0x%x)\n", __FILE__, __FUNCTION__, ptr, (unsigned)size); __bound_new_region(ptr, size); @@ -802,6 +924,7 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) { void *ptr; + WAIT_SEM (); restore_malloc_hooks(); #ifndef HAVE_MEMALIGN @@ -820,11 +943,12 @@ void *__bound_memalign(size_t size, size_t align, const void *caller) #endif install_malloc_hooks(); + POST_SEM (); if (!ptr) return NULL; - dprintf(stderr, "%s, %s calling __bound_new_region(%p, %x)\n", + dprintf(stderr, "%s, %s calling __bound_new_region(%p, 0x%x)\n", __FILE__, __FUNCTION__, ptr, (unsigned)size); __bound_new_region(ptr, size); @@ -836,7 +960,11 @@ void __bound_free(void *ptr, const void *caller) if (ptr == NULL) return; if (__bound_delete_region(ptr) != 0) - bound_error("freeing invalid region"); +#if 0 /* glibc thread code fails with this */ + bound_error("freeing invalid region") +#endif + ; + libc_free(ptr); } @@ -856,7 +984,7 @@ void *__bound_realloc(void *ptr, size_t size, const void *caller) old_size = get_region_size(ptr); if (old_size == EMPTY_SIZE) bound_error("realloc'ing invalid pointer"); - memcpy(ptr1, ptr, old_size); + memcpy(ptr1, ptr, old_size < size ? old_size : size); __bound_free(ptr, caller); return ptr1; } @@ -875,8 +1003,7 @@ void *__bound_calloc(size_t nmemb, size_t size) } #endif -#if 0 -static void bound_dump(void) +void bound_dump(void) { BoundEntry *page, *e; size_t i, j; @@ -888,11 +1015,15 @@ static void bound_dump(void) 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)); + fprintf(stderr, "%016lx:", + (unsigned long) + ((i << (BOUND_T2_BITS + BOUND_T3_BITS)) + + (j << BOUND_T3_BITS))); do { - fprintf(stderr, " %08lx:%08lx", e->start, e->start + e->size); + fprintf(stderr, " %p:%p(%u)", + (void *) e->start, + (void *) (e->start + e->size), + (unsigned)e->is_invalid); e = e->next; } while (e != NULL); fprintf(stderr, "\n"); @@ -900,7 +1031,6 @@ static void bound_dump(void) } } } -#endif /* some useful checked functions */ @@ -918,7 +1048,7 @@ 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", + dprintf(stderr, "%s %s: start, dst=%p src=%p size=0x%x\n", __FILE__, __FUNCTION__, dst, src, (unsigned)size); __bound_check(dst, size); diff --git a/libtcc.c b/libtcc.c index db30223..6aa2afd 100644 --- a/libtcc.c +++ b/libtcc.c @@ -132,7 +132,7 @@ BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) /********************************************************/ /* copy a string and truncate it. */ -ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s) +ST_FUNC char *pstrcpy(char *buf, size_t buf_size, const char *s) { char *q, *q_end; int c; @@ -152,9 +152,9 @@ ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s) } /* strcat and truncate. */ -ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s) +ST_FUNC char *pstrcat(char *buf, size_t buf_size, const char *s) { - int len; + size_t len; len = strlen(buf); if (len < buf_size) pstrcpy(buf + len, buf_size - len, s); diff --git a/riscv64-gen.c b/riscv64-gen.c index 163657c..2359a0e 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 6c70f33..1382e8d 100644 --- a/tcc.h +++ b/tcc.h @@ -493,19 +493,19 @@ typedef struct Sym { int c; /* associated number or Elf symbol index */ union { int sym_scope; /* scope level for locals */ - int jnext; /* next jump label */ + int jnext; /* next jump label */ struct FuncAttr f; /* function attributes */ int auxtype; /* bitfield access type */ }; }; long long enum_val; /* enum constant if IS_ENUM_VAL */ int *d; /* define token stream */ - struct Sym *ncl; /* next cleanup */ + struct Sym *ncl; /* next cleanup */ }; CType type; /* associated type */ union { struct Sym *next; /* next related symbol (for fields and anoms) */ - struct Sym *cleanupstate; /* in defined labels */ + struct Sym *cleanupstate; /* in defined labels */ int asm_label; /* associated asm label */ }; struct Sym *prev; /* prev symbol in stack */ @@ -673,20 +673,20 @@ struct sym_attr { }; struct TCCState { - int verbose; /* if true, display some information during compilation */ - int nostdinc; /* if true, no standard headers are added */ - int nostdlib; /* if true, no standard libraries are added */ - int nocommon; /* if true, do not use common symbols for .bss data */ - int static_link; /* if true, static linking is performed */ - int rdynamic; /* if true, all symbols are exported */ - int symbolic; /* if true, resolve symbols in the current module first */ - int filetype; /* file type for compilation (NONE,C,ASM) */ - int cversion; /* supported C ISO version, 199901 (the default), 201112, ... */ + unsigned char verbose; /* if true, display some information during compilation */ + unsigned char nostdinc; /* if true, no standard headers are added */ + unsigned char nostdlib; /* if true, no standard libraries are added */ + unsigned char nocommon; /* if true, do not use common symbols for .bss data */ + unsigned char static_link; /* if true, static linking is performed */ + unsigned char rdynamic; /* if true, all symbols are exported */ + unsigned char symbolic; /* if true, resolve symbols in the current module first */ + unsigned char filetype; /* file type for compilation (NONE,C,ASM) */ + unsigned int cversion; /* supported C ISO version, 199901 (the default), 201112, ... */ char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */ char *soname; /* as specified on the command line (-soname) */ char *rpath; /* as specified on the command line (-Wl,-rpath=) */ - int enable_new_dtags; /* ditto, (-Wl,--enable-new-dtags) */ + unsigned char enable_new_dtags; /* ditto, (-Wl,--enable-new-dtags) */ /* output type, see TCC_OUTPUT_XXX */ int output_type; @@ -694,25 +694,25 @@ struct TCCState { int output_format; /* C language options */ - int char_is_unsigned; - int leading_underscore; - int ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */ - int dollars_in_identifiers; /* allows '$' char in identifiers */ - int ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */ + unsigned char char_is_unsigned; + unsigned char leading_underscore; + unsigned char ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */ + unsigned char dollars_in_identifiers; /* allows '$' char in identifiers */ + unsigned char ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */ /* warning switches */ - int warn_write_strings; - int warn_unsupported; - int warn_error; - int warn_none; - int warn_implicit_function_declaration; - int warn_gcc_compat; + unsigned char warn_write_strings; + unsigned char warn_unsupported; + unsigned char warn_error; + unsigned char warn_none; + unsigned char warn_implicit_function_declaration; + unsigned char warn_gcc_compat; /* compile with debug symbol (and use them if error during execution) */ - int do_debug; + unsigned char do_debug; #ifdef CONFIG_TCC_BCHECK /* compile with built-in memory and bounds checker */ - int do_bounds_check; + unsigned char do_bounds_check; #endif #ifdef TCC_TARGET_ARM enum float_abi float_abi; /* float ABI of the generated code*/ @@ -720,7 +720,7 @@ struct TCCState { int run_test; /* nth test to run with -dt -run */ addr_t text_addr; /* address of text section */ - int has_text_addr; + unsigned char has_text_addr; unsigned section_align; /* section alignment */ @@ -847,11 +847,11 @@ struct TCCState { int nb_files; /* number thereof */ int nb_libraries; /* number of libs thereof */ char *outfile; /* output filename */ - int option_r; /* option -r */ - int do_bench; /* option -bench */ + unsigned char option_r; /* option -r */ + unsigned char do_bench; /* option -bench */ int gen_deps; /* option -MD */ char *deps_outfile; /* option -MF */ - int option_pthread; /* -pthread option */ + unsigned char option_pthread; /* -pthread option */ int argc; char **argv; }; @@ -1120,8 +1120,8 @@ ST_DATA int tcc_ext; ST_DATA struct TCCState *tcc_state; /* public functions currently used by the tcc main function */ -ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s); -ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s); +ST_FUNC char *pstrcpy(char *buf, size_t buf_size, const char *s); +ST_FUNC char *pstrcat(char *buf, size_t buf_size, const char *s); ST_FUNC char *pstrncpy(char *out, const char *in, size_t num); PUB_FUNC char *tcc_basename(const char *name); PUB_FUNC char *tcc_fileextension (const char *name); @@ -1518,7 +1518,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 70e4f87..e2b9268 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1322,14 +1322,15 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1) #ifdef CONFIG_TCC_BCHECK addr_t *ptr; 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; - set_elf_sym(symtab_section, 0, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + bsym_index = set_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_NOTYPE), 0, bounds_section->sh_num, "__bounds_start"); /* pull bcheck.o from libtcc1.a */ sym_index = set_elf_sym(symtab_section, 0, 0, @@ -1344,6 +1345,39 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1) 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_I386 + 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); +#else + pinit = section_ptr_add(init_section, 13); + pinit[0] = 0x48; /* mov xx,%rax */ + pinit[1] = 0xb8; + write64le(pinit + 2, 0); + pinit[10] = 0x48; /* mov %rax,%rdi */ + pinit[11] = 0x89; + pinit[12] = 0xc7; + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 11, R_X86_64_64, bsym_index); +#endif + 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_I386 + pinit = section_ptr_add(init_section, 3); + pinit[0] = 0x83; /* add $0x4,%esp */ + pinit[1] = 0xc4; + pinit[2] = 0x04; +#endif } #endif } @@ -1366,7 +1400,12 @@ ST_FUNC void tcc_add_runtime(TCCState *s1) tcc_add_dll(s1, TCC_LIBGCC, 0); } #endif - tcc_add_support(s1, TCC_LIBTCC1); +#ifdef CONFIG_TCC_BCHECK + if (s1->do_bounds_check) + tcc_add_library_err(s1, "pthread"); + if (s1->do_bounds_check == 0 || s1->output_type != TCC_OUTPUT_DLL) +#endif + tcc_add_support(s1, TCC_LIBTCC1); /* add crt end if not memory output */ if (s1->output_type != TCC_OUTPUT_MEMORY) tcc_add_crt(s1, "crtn.o"); @@ -2814,6 +2853,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,9 +2865,14 @@ 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) diff --git a/tccgen.c b/tccgen.c index a6181b0..fe2121b 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1191,7 +1191,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 +1379,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); @@ -7413,7 +7417,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 +7427,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--; @@ -7599,7 +7605,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/tests/Makefile b/tests/Makefile index 9a4d123..ad8ab63 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -158,7 +158,8 @@ btest: boundtest.c @echo ------------ $@ ------------ @for i in $(BOUNDS_OK); do \ echo ; echo --- boundtest $$i ---; \ - if $(TCC) -b -run $< $$i ; then \ + $(TCC) -b $< -o boundtest; \ + if ./boundtest $$i ; then \ echo succeeded as expected; \ else\ echo Failed positive test $$i ; exit 1 ; \ @@ -166,8 +167,9 @@ btest: boundtest.c done ;\ for i in $(BOUNDS_FAIL); do \ echo ; echo --- boundtest $$i ---; \ - if $(TCC) -b -run $< $$i ; then \ - echo Failed negative test $$i ; exit 1 ;\ + $(TCC) -b $< -o boundtest; \ + if ./boundtest $$i ; then \ + echo Failed negative test $$i ; exit 1 ; \ else\ echo failed as expected; \ fi ;\ diff --git a/tests/boundtest.c b/tests/boundtest.c index e5c3ff4..ec27bf2 100644 --- a/tests/boundtest.c +++ b/tests/boundtest.c @@ -253,10 +253,20 @@ int (*table_test[])(void) = { int main(int argc, char **argv) { + int i; + char *cp; int index; int (*ftest)(void); int index_max = sizeof(table_test)/sizeof(table_test[0]); + /* check bounds checking main arg */ + for (i = 0; i < argc; i++) { + cp = argv[i]; + while (*cp) { + cp++; + } + } + if (argc < 2) { printf( "test TCC bound checking system\n" diff --git a/tests/tcctest.c b/tests/tcctest.c index e4645bb..db4789c 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -123,6 +123,7 @@ void math_cmp_test(void); void callsave_test(void); void builtin_frame_address_test(void); void attrib_test(void); +void bounds_check1_test(void); int fib(int n); void num(int n); @@ -771,6 +772,7 @@ int main(int argc, char **argv) if (via_volatile (42) != 42) printf ("via_volatile broken\n"); attrib_test(); + bounds_check1_test(); return 0; } @@ -3884,9 +3886,11 @@ void builtin_frame_address_test(void) printf("str: %s\n", str); #ifndef __riscv +#ifndef __BOUNDS_CHECKING_ON bfa1(str-fp0); #endif #endif +#endif } char via_volatile (char i) @@ -3961,3 +3965,18 @@ int force_get_order(unsigned long s) { return __get_order(s); } + +#define pv(m) printf(sizeof (s->m + 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/x86_64-gen.c b/x86_64-gen.c index cc66b60..677d8fb 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -641,11 +641,15 @@ 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 } /* generate a bounded pointer addition */ @@ -654,6 +658,10 @@ ST_FUNC void gen_bounded_ptr_add(void) /* save all temporary registers */ save_regs(0); + 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); o(0xc68948); // mov %rax,%rsi ## second arg in %rsi, this must be size @@ -664,12 +672,15 @@ ST_FUNC void gen_bounded_ptr_add(void) vtop--; /* do a fast function call */ - gen_static_call(TOK___bound_ptr_add); + gen_bounds_call(TOK___bound_ptr_add); /* returned pointer is in rax */ vtop++; vtop->r = TREG_RAX | VT_BOUNDED; + 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))); @@ -935,8 +946,9 @@ void gfunc_call(int nb_args) #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; @@ -1430,11 +1442,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 +1532,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 +1589,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(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 } @@ -1603,17 +1623,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 +1961,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 +2029,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)); diff --git a/tests/tests2/110_average.c b/tests/tests2/110_average.c new file mode 100644 index 0000000..273b511 --- /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 0000000..4955335 --- /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 0000000..c0815e1 --- /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 0000000..d81cc07 --- /dev/null +++ b/tests/tests2/111_conversion.expect @@ -0,0 +1 @@ +42