From c88b19966c61bfeeeb9b9b092582496875f3d7f5 Mon Sep 17 00:00:00 2001 From: grischka Date: Wed, 14 Feb 2024 10:00:22 +0100 Subject: [PATCH] tccrun: exit() via rt_longjmp() - new LIBTCC API tcc_setjmp() to allow longjmps & signals from compiled code back to libtcc per TCCState - new LIBTCC API tcc_set_backtrace_func() to handle backtrace output - move c/dtor/atexit stuff to runtime (lib/runmain.c) - move bt-log.o into libtcc1.a - add timeouts to github action (beware, it did happen to hang infinitely in the signal handler at some point) --- .github/workflows/build.yml | 6 + Makefile | 28 ++- lib/Makefile | 3 +- lib/bt-exe.c | 12 +- lib/bt-log.c | 9 +- lib/runmain.c | 87 ++++++++ libtcc.h | 11 + tcc.h | 4 + tccelf.c | 2 - tccpe.c | 2 - tccrun.c | 334 +++++++++++++++-------------- tests/Makefile | 28 +-- tests/libtcc_test_mt.c | 42 +++- tests/tests2/128_run_atexit.c | 5 + tests/tests2/128_run_atexit.expect | 2 + win32/lib/crt1.c | 30 ++- 16 files changed, 395 insertions(+), 210 deletions(-) create mode 100644 lib/runmain.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5fda5d93..e5a8e760 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,6 +7,7 @@ on: jobs: test-x86_64-linux: runs-on: ubuntu-20.04 + timeout-minutes: 2 steps: - uses: actions/checkout@v4 - name: make & test tcc @@ -14,6 +15,7 @@ jobs: test-x86_64-osx: runs-on: macos-11 + timeout-minutes: 2 steps: - uses: actions/checkout@v4 - name: make & test tcc @@ -21,6 +23,7 @@ jobs: test-x86_64-win32: runs-on: windows-2019 + timeout-minutes: 4 steps: - uses: actions/checkout@v4 - name: make & test tcc @@ -33,6 +36,7 @@ jobs: test-armv7-linux: runs-on: ubuntu-20.04 + timeout-minutes: 6 steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v2 @@ -49,6 +53,7 @@ jobs: test-aarch64-linux: runs-on: ubuntu-20.04 + timeout-minutes: 6 steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v2 @@ -65,6 +70,7 @@ jobs: test-riscv64-linux: runs-on: ubuntu-20.04 + timeout-minutes: 6 steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v2 diff --git a/Makefile b/Makefile index 652bf2d3..98f01e49 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,9 @@ ifdef CONFIG_WIN32 ifneq ($(CONFIG_static),yes) LIBTCC = libtcc$(DLLSUF) LIBTCCDEF = libtcc.def + -LTCC = $(bindir)/libtcc.dll + else + -LTCC = -ltcc -L$(libdir) endif ifneq ($(CONFIG_debug),yes) LDFLAGS += -s @@ -70,6 +73,7 @@ else endif export MACOSX_DEPLOYMENT_TARGET := 10.6 endif + -LTCC = -ltcc endif # run local version of tcc with local libraries and includes @@ -79,6 +83,15 @@ TCCFLAGS = $(TCCFLAGS$(CFG)) TCC_LOCAL = $(TOP)/tcc$(EXESUF) TCC = $(TCC_LOCAL) $(TCCFLAGS) +# run tests with the installed tcc instead +ifdef TESTINSTALL + TCC_LOCAL = $(bindir)/tcc + TCCFLAGS-unx = -I.. + TCCFLAGS-win = -I.. -B$(bindir) + LIBTCC = + LIBS += $(-LTCC) +endif + CFLAGS_P = $(CFLAGS) -pg -static -DCONFIG_TCC_STATIC -DTCC_PROFILE LIBS_P = $(LIBS) LDFLAGS_P = $(LDFLAGS) @@ -373,7 +386,7 @@ IR = $(IM) mkdir -p $2 && cp -r $1/. $2 IM = @echo "-> $2 : $1" ; BINCHECK = $(if $(wildcard $(PROGS) *-tcc$(EXESUF)),,@echo "Makefile: nothing found to install" && exit 1) -B_O = bcheck.o bt-exe.o bt-log.o bt-dll.o +B_O = runmain.o bt-exe.o bt-dll.o bcheck.o # install progs & libs install-unx: @@ -411,7 +424,7 @@ install-win: $(call IR,$(TOPSRC)/win32/include,"$(tccdir)/include") $(call IR,$(TOPSRC)/win32/examples,"$(tccdir)/examples") $(call IF,$(TOPSRC)/tests/libtcc_test.c,"$(tccdir)/examples") - $(call IFw,$(TOPSRC)/libtcc.h libtcc.def,"$(libdir)") + $(call IFw,$(TOPSRC)/libtcc.h libtcc.def libtcc.a,"$(libdir)") $(call IFw,$(TOPSRC)/win32/tcc-win32.txt tcc-doc.html,"$(docdir)") ifneq "$(wildcard $(LIBTCC1_U))" "" $(call IFw,$(LIBTCC1_U),"$(tccdir)/lib") @@ -420,9 +433,9 @@ endif # uninstall on windows uninstall-win: - @rm -fv $(addprefix "$(bindir)/", libtcc*.dll $(PROGS) *-tcc.exe) - @rm -fr $(foreach P,doc examples include lib libtcc,"$(tccdir)/$P/*") - @rm -frv $(addprefix "$(tccdir)/", doc examples include lib libtcc) + @rm -fv $(foreach P,libtcc*.dll $(PROGS) *-tcc.exe,"$(bindir)"/$P) + @rm -fr $(foreach P,doc examples include lib libtcc,"$(tccdir)"/$P/*) + @rm -frv $(foreach P,doc examples include lib libtcc,"$(tccdir)"/$P) # the msys-git shell works to configure && make except it does not have install ifeq ($(OS),Windows_NT) @@ -473,6 +486,9 @@ tcov-tes% : tcc_c$(EXESUF) @$(MAKE) --no-print-directory TCC_LOCAL=$(CURDIR)/$< tes$* tcc_c$(EXESUF): $($T_FILES) $S$(TCC) tcc.c -o $@ -ftest-coverage $(DEFINES) $(LIBS) +# test the installed tcc instead +test-install: tccdefs_.h + @$(MAKE) -C tests TESTINSTALL=yes #_all clean: @rm -f tcc$(EXESUF) tcc_c$(EXESUF) tcc_p$(EXESUF) *-tcc$(EXESUF) @@ -506,6 +522,8 @@ help: @echo " run all/single test(s) from tests/pp" @echo "make tcov-test / tcov-tests2... / tcov-testspp..." @echo " run tests as above with code coverage. After test(s) see tcc_c$(EXESUF).tcov" + @echo "make test-install" + @echo " run tests with the installed tcc" @echo "Other supported make targets:" @echo " install install-strip doc clean tags ETAGS tar distclean help" @echo "Custom configuration:" diff --git a/lib/Makefile b/lib/Makefile index eba3a6b4..1a232237 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,7 +67,8 @@ OBJ-arm-eabihf = $(ARM_O) $(DSO_O) OBJ-arm-wince = $(ARM_O) $(WIN_O) OBJ-riscv64 = $(RISCV64_O) $(BCHECK_O) $(DSO_O) -OBJ-extra = $(filter $(B_O),$(OBJ-$T)) +OBJ-extra = $(filter-out bt-log.o,$(filter $(B_O),$(OBJ-$T))) +OBJ-extra += runmain.o OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T))) ALL = $(addprefix $(TOP)/,$(X)libtcc1.a $(OBJ-extra)) diff --git a/lib/bt-exe.c b/lib/bt-exe.c index 2960dd00..447eaf97 100644 --- a/lib/bt-exe.c +++ b/lib/bt-exe.c @@ -26,14 +26,14 @@ void __bt_init(rt_context *p, int is_exe) __bound_init(p->bounds_start, -1); /* add to chain */ - WAIT_SEM(&rt_sem); + rt_wait_sem(); p->next = g_rc, g_rc = p; - if (is_exe) + rt_post_sem(); + if (is_exe) { /* we are the executable (not a dll) */ p->top_func = main; - POST_SEM(&rt_sem); - if (is_exe) set_exception_handler(); + } } __declspec(dllexport) @@ -48,13 +48,13 @@ void __bt_exit(rt_context *p) __bound_exit_dll(p->bounds_start); /* remove from chain */ - WAIT_SEM(&rt_sem); + rt_wait_sem(); for (pp = &g_rc; rc = *pp, rc; pp = &rc->next) if (rc == p) { *pp = rc->next; break; } - POST_SEM(&rt_sem); + rt_post_sem(); } /* copy a string and truncate it. */ diff --git a/lib/bt-log.c b/lib/bt-log.c index 05f7c40e..87e411d5 100644 --- a/lib/bt-log.c +++ b/lib/bt-log.c @@ -18,25 +18,24 @@ #pragma GCC diagnostic ignored "-Wframe-address" #endif -typedef struct rt_frame -{ +typedef struct rt_frame { void *ip, *fp, *sp; } rt_frame; __attribute__((weak)) -int _rt_error(rt_frame *f, const char *msg, const char *fmt, va_list ap); +int __rt_dump(rt_frame *f, const char *msg, const char *fmt, va_list ap); DLL_EXPORT int tcc_backtrace(const char *fmt, ...) { va_list ap; int ret; - if (_rt_error) { + if (__rt_dump) { rt_frame f; f.fp = __builtin_frame_address(1); f.ip = __builtin_return_address(0); va_start(ap, fmt); - ret = _rt_error(&f, "", fmt, ap); + ret = __rt_dump(&f, NULL, fmt, ap); va_end(ap); } else { const char *p, *nl = "\n"; diff --git a/lib/runmain.c b/lib/runmain.c new file mode 100644 index 00000000..5ebeeec3 --- /dev/null +++ b/lib/runmain.c @@ -0,0 +1,87 @@ +/* ------------------------------------------------------------- */ +/* support for tcc_run() */ + +#ifdef __leading_underscore +# define _(s) s +#else +# define _(s) _##s +#endif + +#ifndef _WIN32 +extern void (*_(_init_array_start)[]) (int argc, char **argv, char **envp); +extern void (*_(_init_array_end)[]) (int argc, char **argv, char **envp); +static void run_ctors(int argc, char **argv, char **env) +{ + int i = 0; + while (&_(_init_array_start)[i] != _(_init_array_end)) + (*_(_init_array_start)[i++])(argc, argv, env); +} +#endif + +extern void (*_(_fini_array_start)[]) (void); +extern void (*_(_fini_array_end)[]) (void); +static void run_dtors(void) +{ + int i = 0; + while (&_(_fini_array_end)[i] != _(_fini_array_start)) + (*_(_fini_array_end)[--i])(); +} + +static void *rt_exitfunc[32]; +static void *rt_exitarg[32]; +int __rt_nr_exit; + +void __run_on_exit(int ret) +{ + int n = __rt_nr_exit; + while (n) + --n, ((void(*)(int,void*))rt_exitfunc[n])(ret, rt_exitarg[n]); +} + +int on_exit(void *function, void *arg) +{ + int n = __rt_nr_exit; + if (n < 32) { + rt_exitfunc[n] = function; + rt_exitarg[n] = arg; + __rt_nr_exit = n + 1; + return 0; + } + return 1; +} + +int atexit(void (*function)(void)) +{ + return on_exit(function, 0); +} + +typedef struct rt_frame { + void *ip, *fp, *sp; +} rt_frame; + +void __rt_longjmp(rt_frame *, int); + +void exit(int code) +{ + rt_frame f; + run_dtors(); + __run_on_exit(code); + f.fp = __builtin_frame_address(1); + f.ip = __builtin_return_address(0); + __rt_longjmp(&f, code); +} + +#ifndef _WIN32 +int main(int, char**, char**); + +int _runmain(int argc, char **argv, char **envp) +{ + int ret; + __rt_nr_exit = 0; + run_ctors(argc, argv, envp); + ret = main(argc, argv, envp); + run_dtors(); + __run_on_exit(ret); + return ret; +} +#endif diff --git a/libtcc.h b/libtcc.h index 205ed154..7e032d80 100644 --- a/libtcc.h +++ b/libtcc.h @@ -100,6 +100,17 @@ LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name); LIBTCCAPI void tcc_list_symbols(TCCState *s, void *ctx, void (*symbol_cb)(void *ctx, const char *name, const void *val)); +/* experimental/advanced section (see libtcc_test_mt.c for an example) */ + +/* catch runtime exceptions (optionally limit backtraces at top_func), + when using tcc_set_options("-bt") and when not using tcc_run() */ +LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *longjmp, void *jmp_buf, void *top_func); +#define tcc_setjmp(s1,jb,f) setjmp(_tcc_setjmp(s1, longjmp, jb, f)) + +/* set custom error printer for runtime exceptions */ +typedef void TCCBtFunc(void *pc, const char *file, int line, const char* func); +LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, TCCBtFunc*); + #ifdef __cplusplus } #endif diff --git a/tcc.h b/tcc.h index 837dc6e2..16f316d2 100644 --- a/tcc.h +++ b/tcc.h @@ -1000,6 +1000,10 @@ struct TCCState { #ifdef _WIN64 void *run_function_table; /* unwind data */ #endif + struct TCCState *next; + struct rt_context *rc; /* pointer to backtrace info block */ + void *run_lj, *run_jb; /* sj/lj for tcc_setjmp()/tcc_run() */ + void (*bt_func)(void *, const char*, int, const char*); #endif #ifdef CONFIG_TCC_BACKTRACE diff --git a/tccelf.c b/tccelf.c index 2ccf9816..049d674f 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1778,8 +1778,6 @@ ST_FUNC void tcc_add_runtime(TCCState *s1) if (s1->do_backtrace) { if (s1->output_type & TCC_OUTPUT_EXE) tcc_add_support(s1, "bt-exe.o"); - if (s1->output_type != TCC_OUTPUT_DLL) - tcc_add_support(s1, "bt-log.o"); tcc_add_btstub(s1); lpthread = 1; } diff --git a/tccpe.c b/tccpe.c index 936f1468..d2a2c8b3 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1961,8 +1961,6 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe) tcc_add_support(s1, "bt-exe.o"); if (s1->output_type == TCC_OUTPUT_DLL) tcc_add_support(s1, "bt-dll.o"); - if (s1->output_type != TCC_OUTPUT_DLL) - tcc_add_support(s1, "bt-log.o"); tcc_add_btstub(s1); } #endif diff --git a/tccrun.c b/tccrun.c index 54aa9d65..6ba61a64 100644 --- a/tccrun.c +++ b/tccrun.c @@ -26,7 +26,7 @@ #ifdef CONFIG_TCC_BACKTRACE typedef struct rt_context { - /* --> tccelf.c:tcc_add_btstub wants those below in that order: */ + /* tccelf.c:tcc_add_btstub() wants these in that order: */ union { struct { Stab_Sym *stab_sym; @@ -42,55 +42,33 @@ typedef struct rt_context ElfW(Sym) *esym_start; ElfW(Sym) *esym_end; char *elf_str; - + // 6 addr_t prog_base; void *bounds_start; void *top_func; TCCState *s1; struct rt_context *next; - + // 11 int num_callers; int dwarf; - /* <-- */ } rt_context; /* size = 11 * PTR_SIZE + 2 * sizeof (int) */ -typedef struct rt_frame -{ +typedef struct rt_frame { addr_t ip, fp, sp; } rt_frame; /* linked list of rt_contexts */ static rt_context *g_rc; +static TCCState *g_s1; /* semaphore to protect it */ TCC_SEM(static rt_sem); static int signal_set; static void set_exception_handler(void); - -#ifdef _WIN32 -#define ATTR_WEAK -#else -#define ATTR_WEAK __attribute__((weak)) -#endif -int ATTR_WEAK _rt_error(rt_frame *f, const char *msg, const char *fmt, va_list ap); -void ATTR_WEAK rt_wait_sem(void) { WAIT_SEM(&rt_sem); } -void ATTR_WEAK rt_post_sem(void) { POST_SEM(&rt_sem); } -#undef ATTR_WEAK +static void rt_wait_sem(void) { WAIT_SEM(&rt_sem); } +static void rt_post_sem(void) { POST_SEM(&rt_sem); } +static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level); #endif /* def CONFIG_TCC_BACKTRACE */ -/* handle exit/atexit for tcc_run() -- thread-unsafe */ -static jmp_buf rt_jb; -static int rt_do_jmp; -static int rt_nr_exit; -static void *rt_exitfunc[32]; -static void *rt_exitarg[32]; - -static void rt_exit(int code) -{ - if (rt_do_jmp) - longjmp(rt_jb, code ? code : 256); - exit(code); -} - /* ------------------------------------------------------------- */ /* defined when included from lib/bt-exe.c */ #ifndef CONFIG_TCC_BACKTRACE_ONLY @@ -101,7 +79,8 @@ static void rt_exit(int code) static int protect_pages(void *ptr, unsigned long length, int mode); static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff); - +static int __rt_dump(rt_frame *f, const char *msg, const char *fmt, va_list ap); +static void rt_longjmp(rt_frame *f, int code); #ifdef _WIN64 static void *win64_add_function_table(TCCState *s1); static void win64_del_function_table(void *); @@ -118,12 +97,9 @@ static void bt_link(TCCState *s1) rc = tcc_get_symbol(s1, "__rt_info"); if (!rc) return; - rt_wait_sem(); - rc->next = g_rc, g_rc = rc, rc->s1 = s1; rc->esym_start = (ElfW(Sym) *)(symtab_section->data); rc->esym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); rc->elf_str = (char *)symtab_section->link->data; - rc->top_func = tcc_get_symbol(s1, "main"); if (PTR_SIZE == 8 && !s1->dwarf) rc->prog_base &= 0xffffffff00000000ULL; #ifdef CONFIG_TCC_BCHECK @@ -132,7 +108,7 @@ static void bt_link(TCCState *s1) ((void(*)(void*,int))p)(rc->bounds_start, 1); } #endif - rt_post_sem(); + rc->next = g_rc, g_rc = rc, rc->s1 = s1, s1->rc = rc; if (0 == signal_set) set_exception_handler(), signal_set = 1; #endif @@ -142,16 +118,35 @@ static void bt_unlink(TCCState *s1) { #ifdef CONFIG_TCC_BACKTRACE rt_context *rc, **pp; - rt_wait_sem(); for (pp = &g_rc; rc = *pp, rc; pp = &rc->next) if (rc->s1 == s1) { *pp = rc->next; break; } - rt_post_sem(); #endif } +static void st_link(TCCState *s1) +{ + rt_wait_sem(); + s1->next = g_s1, g_s1 = s1; + bt_link(s1); + rt_post_sem(); +} + +static void st_unlink(TCCState *s1) +{ + TCCState *s2, **pp; + rt_wait_sem(); + bt_unlink(s1); + for (pp = &g_s1; s2 = *pp, s2; pp = &s2->next) + if (s2 == s1) { + *pp = s2->next; + break; + } + rt_post_sem(); +} + #if !_WIN32 && !__APPLE__ //#define HAVE_SELINUX 1 #endif @@ -196,9 +191,8 @@ LIBTCCAPI int tcc_relocate(TCCState *s1) exit(tcc_error_noabort("'tcc_relocate()' twice is no longer supported")); #ifdef CONFIG_TCC_BACKTRACE - /* for bt-log.c (but not when 'tcc -bt -run tcc.c') */ - if (s1->do_backtrace && !tcc_get_symbol(s1, "_rt_error")) - tcc_add_symbol(s1, "_rt_error", _rt_error); + if (s1->do_backtrace) + tcc_add_symbol(s1, "__rt_dump", __rt_dump); /* for bt-log.c */ #endif size = tcc_relocate_ex(s1, NULL, 0); @@ -209,7 +203,7 @@ LIBTCCAPI int tcc_relocate(TCCState *s1) return -1; ret = tcc_relocate_ex(s1, s1->run_ptr, ptr_diff); if (ret == 0) - bt_link(s1); + st_link(s1); return ret; } @@ -231,12 +225,11 @@ ST_FUNC void tcc_run_free(TCCState *s1) } /* free loaded dlls array */ dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls); - /* unmap or unprotect and free memory */ ptr = s1->run_ptr; if (NULL == ptr) return; - bt_unlink(s1); + st_unlink(s1); size = s1->run_size; #ifdef HAVE_SELINUX munmap(ptr, size * 2); @@ -250,41 +243,25 @@ ST_FUNC void tcc_run_free(TCCState *s1) #endif } -static void run_cdtors(TCCState *s1, const char *start, const char *end, - int argc, char **argv, char **envp) +LIBTCCAPI void *_tcc_setjmp(TCCState *s1, void *p_longjmp, void *p_jmp_buf, void *func) { - void **a = (void **)get_sym_addr(s1, start, 0, 0); - void **b = (void **)get_sym_addr(s1, end, 0, 0); - while (a != b) - ((void(*)(int, char **, char **))*a++)(argc, argv, envp); + s1->run_lj = p_longjmp; + s1->run_jb = p_jmp_buf; + if (func && s1->rc) + s1->rc->top_func = func; + return p_jmp_buf; } -static void run_on_exit(int ret) +LIBTCCAPI void tcc_set_backtrace_func(TCCState *s1, TCCBtFunc *func) { - int n = rt_nr_exit; - while (n) - --n, ((void(*)(int,void*))rt_exitfunc[n])(ret, rt_exitarg[n]); -} - -static int rt_on_exit(void *function, void *arg) -{ - if (rt_nr_exit < countof(rt_exitfunc)) { - rt_exitfunc[rt_nr_exit] = function; - rt_exitarg[rt_nr_exit++] = arg; - return 0; - } - return 1; -} - -static int rt_atexit(void *function) -{ - return rt_on_exit(function, NULL); + s1->bt_func = func; } /* launch the compiled program with the given arguments */ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) { int (*prog_main)(int, char **, char **), ret; + jmp_buf main_jb; #if defined(__APPLE__) || defined(__FreeBSD__) char **envp = NULL; @@ -295,35 +272,29 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) char **envp = environ; #endif - s1->run_main = s1->nostdlib ? "_start" : "main"; - if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->run_main, 0, 1)) + /* tcc -dt -run ... nothing to do if no main() */ + if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, "main", 0, 1)) return 0; - tcc_add_symbol(s1, "exit", rt_exit); - tcc_add_symbol(s1, "atexit", rt_atexit); - tcc_add_symbol(s1, "on_exit", rt_on_exit); + tcc_add_support(s1, "runmain.o"); + tcc_add_symbol(s1, "__rt_longjmp", rt_longjmp); + s1->run_main = (s1->nostdlib ? "_start" : "_runmain"); if (tcc_relocate(s1) < 0) return -1; prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1); if ((addr_t)-1 == (addr_t)prog_main) return -1; - errno = 0; /* clean errno value */ fflush(stdout); fflush(stderr); - rt_do_jmp = 1; - rt_nr_exit = 0; - /* These aren't C symbols, so don't need leading underscore handling. */ - run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp); - ret = setjmp(rt_jb); + ret = tcc_setjmp(s1, main_jb, tcc_get_symbol(s1, "main")); if (0 == ret) ret = prog_main(argc, argv, envp); else if (256 == ret) ret = 0; - run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL); - run_on_exit(ret); + if (s1->dflag & 16 && ret) /* tcc -dt -run ... */ fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp); return ret; @@ -385,7 +356,6 @@ static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff) addr_t mem, addr; if (NULL == ptr) { - s1->nb_errors = 0; #ifdef TCC_TARGET_PE pe_output_file(s1, NULL); #else @@ -616,7 +586,7 @@ static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr) /* print the position in the source file of PC value 'pc' by reading the stabs debug information */ static addr_t rt_printline (rt_context *rc, addr_t wanted_pc, - const char *msg, const char *skip) + rt_context** prc, const char *msg, const char *skip) { char func_name[128]; addr_t func_addr, last_pc, pc; @@ -726,14 +696,21 @@ next: found: i = last_incl_index; + str = NULL; if (i > 0) { str = incl_files[--i]; if (skip[0] && strstr(str, skip)) return (addr_t)-1; - rt_printf("%s:%d: ", str, last_line_num); - } else - rt_printf("%08llx : ", (long long)wanted_pc); - rt_printf("%s %s", msg, func_name[0] ? func_name : "???"); + } + if (rc && rc->s1 && rc->s1->bt_func) { + rc->s1->bt_func((void*)wanted_pc, str, last_line_num, func_name[0] ? func_name : NULL); + } else { + if (str) + rt_printf("%s:%d: ", str, last_line_num); + else + rt_printf("%08llx : ", (long long)wanted_pc); + rt_printf("%s %s", msg, func_name[0] ? func_name : "???"); + } #if 0 if (--i >= 0) { rt_printf(" (included from "); @@ -746,6 +723,7 @@ found: rt_printf(")"); } #endif + *prc = rc; return func_addr; } @@ -816,7 +794,7 @@ dwarf_read_sleb128(unsigned char **ln, unsigned char *end) } static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc, - const char *msg, const char *skip) + rt_context** prc, const char *msg, const char *skip) { unsigned char *ln; unsigned char *cp; @@ -859,6 +837,7 @@ static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc, next: filename = NULL; func_addr = 0; + line = 0; if (NULL == rc) goto found; @@ -1102,29 +1081,33 @@ next_line: goto next; found: - if (filename) { - if (skip[0] && strstr(filename, skip)) - return (addr_t)-1; - rt_printf("%s:%d: ", filename, line); + if (rc && rc->s1 && rc->s1->bt_func) { + rc->s1->bt_func((void*)wanted_pc, filename, line, function); + } else { + if (filename) { + if (skip[0] && strstr(filename, skip)) + return (addr_t)-1; + rt_printf("%s:%d: ", filename, line); + } + else + rt_printf("0x%08llx : ", (long long)wanted_pc); + rt_printf("%s %s", msg, function ? function : "???"); } - else - rt_printf("0x%08llx : ", (long long)wanted_pc); - rt_printf("%s %s", msg, function ? function : "???"); + *prc = rc; return (addr_t)func_addr; } /* ------------------------------------------------------------- */ - -static int rt_get_caller_pc(addr_t *paddr, rt_frame *f, int level); - -int _rt_error(rt_frame *f, const char *msg, const char *fmt, va_list ap) +#ifndef CONFIG_TCC_BACKTRACE_ONLY +static +#endif +int __rt_dump(rt_frame *f, const char *msg, const char *fmt, va_list ap) { - rt_context *rc; + rt_context *rc, *rd; addr_t pc = 0; char skip[100]; int i, level, ret, n, one; const char *a, *b; - addr_t (*printline)(rt_context*, addr_t, const char*, const char*); - addr_t top_func = 0; + addr_t (*printline)(rt_context*, addr_t, rt_context**, const char*, const char*); skip[0] = 0; /* If fmt is like "^file.c^..." then skip calls from 'file.c' */ @@ -1145,27 +1128,28 @@ int _rt_error(rt_frame *f, const char *msg, const char *fmt, va_list ap) printline = rt_printline_dwarf; if (rc->num_callers) n = rc->num_callers; - top_func = (addr_t)rc->top_func; } - for (i = level = 0; level < n; i++) { ret = rt_get_caller_pc(&pc, f, i); - a = "%s"; if (ret != -1) { - pc = printline(rc, pc, level ? "by" : "at", skip); + pc = printline(rc, pc, &rd, level ? "by" : "at", skip); if (pc == (addr_t)-1) continue; - a = ": %s"; } if (level == 0) { - rt_printf(a, msg); + if (rd && rd->s1 && rd->s1->bt_func) + break; + if (ret != -1) + rt_printf(": "); + if (msg) + rt_printf("%s: ", msg); rt_vprintf(fmt, ap); } else if (ret == -1) break; if (one) break; rt_printf("\n"); - if (ret == -1 || (pc == top_func && pc)) + if (ret == -1 || (rd && pc == (addr_t)rd->top_func && pc)) break; ++level; } @@ -1180,11 +1164,46 @@ static int rt_error(rt_frame *f, const char *fmt, ...) va_list ap; int ret; va_start(ap, fmt); - ret = _rt_error(f, "RUNTIME ERROR: ", fmt, ap); + ret = __rt_dump(f, "RUNTIME ERROR", fmt, ap); va_end(ap); return ret; } +static TCCState *rt_find_state(rt_frame *f) +{ + TCCState *s; + int level; + addr_t pc; + + rt_wait_sem(); + for (s = g_s1; s; s = s->next) { + if (0 == s->run_lj) + continue; + for (level = 0; level < 8; ++level) { + if (rt_get_caller_pc(&pc, f, level) < 0) + break; + if (pc >= (addr_t)s->run_ptr + && pc < (addr_t)s->run_ptr + s->run_size) + goto found; + } + } +found: + rt_post_sem(); + //fprintf(stderr, "\nrt_state found %s %p %p\n", s ? "YES" : "NO", s, s->rc->top_func), fflush(stderr); + return s; +} + +static void rt_longjmp(rt_frame *f, int code) +{ + TCCState *s = rt_find_state(f); + if (s && s->run_lj) { + if (code == 0) + code = 256; + ((void(*)(void*,int))s->run_lj)(s->run_jb, code); + } + exit(code); +} + /* ------------------------------------------------------------- */ #ifndef _WIN32 @@ -1319,7 +1338,8 @@ static void sig_error(int signum, siginfo_t *siginf, void *puc) rt_error(&f, "caught signal %d", signum); break; } - rt_exit(255); + set_exception_handler(); + rt_longjmp(&f, 255); } #ifndef SA_SIGINFO @@ -1333,7 +1353,7 @@ static void set_exception_handler(void) /* install TCC signal handlers to print debug info on fatal runtime errors */ sigemptyset (&sigact.sa_mask); - sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; + sigact.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_NODEFER; #if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes sigact.sa_flags |= SA_ONSTACK; #endif @@ -1365,7 +1385,6 @@ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) { rt_frame f; unsigned code; - rt_getcontext(ex_info->ContextRecord, &f); switch (code = ex_info->ExceptionRecord->ExceptionCode) { @@ -1387,8 +1406,7 @@ static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) rt_error(&f, "caught exception %08x", code); break; } - if (rt_do_jmp) - rt_exit(255); + rt_longjmp(&f, 255); return EXCEPTION_EXECUTE_HANDLER; } @@ -1405,45 +1423,41 @@ static void set_exception_handler(void) #if defined(__i386__) || defined(__x86_64__) static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level) { - addr_t ip, fp; - if (level == 0) { - ip = rc->ip; - } else { - ip = 0; - fp = rc->fp; - while (--level) { - /* XXX: check address validity with program info */ - if (fp <= 0x1000) - break; - fp = ((addr_t *)fp)[0]; - } - if (fp > 0x1000) - ip = ((addr_t *)fp)[1]; - } - if (ip <= 0x1000) - return -1; - *paddr = ip; - return 0; -} - -#elif defined(__arm__) -static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level) -{ - /* XXX: only supports linux/bsd */ -#if !defined(__linux__) && \ - !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) - return -1; -#else if (level == 0) { *paddr = rc->ip; } else { addr_t fp = rc->fp; - while (--level) + while (1) { + if (fp < 0x1000) + return -1; + if (0 == --level) + break; + /* XXX: check address validity with program info */ fp = ((addr_t *)fp)[0]; + } + *paddr = ((addr_t *)fp)[1]; + } + return 0; +} + +/* XXX: only supports linux/bsd */ +#elif defined(__arm__) && !defined(_WIN32) +static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level) +{ + if (level == 0) { + *paddr = rc->ip; + } else { + addr_t fp = rc->fp; + while (1) { + if (fp < 0x1000) + return -1; + if (0 == --level) + break; + fp = ((addr_t *)fp)[0]; + } *paddr = ((addr_t *)fp)[2]; } return 0; -#endif } #elif defined(__aarch64__) @@ -1452,10 +1466,15 @@ static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level) if (level == 0) { *paddr = rc->ip; } else { - addr_t *fp = (addr_t*)rc->fp; - while (--level) - fp = (addr_t *)fp[0]; - *paddr = fp[1]; + addr_t fp = rc->fp; + while (1) { + if (fp < 0x1000) + return -1; + if (0 == --level) + break; + fp = ((addr_t *)fp)[0]; + } + *paddr = ((addr_t *)fp)[1]; } return 0; } @@ -1466,12 +1485,15 @@ static int rt_get_caller_pc(addr_t *paddr, rt_frame *rc, int level) if (level == 0) { *paddr = rc->ip; } else { - addr_t *fp = (addr_t*)rc->fp; - while (--level && fp >= (addr_t*)0x1000) - fp = (addr_t *)fp[-2]; - if (fp < (addr_t*)0x1000) - return -1; - *paddr = fp[-1]; + addr_t fp = rc->fp; + while (1) { + if (fp < 0x1000) + return -1; + if (0 == --level) + break; + fp = ((addr_t *)fp)[-2]; + } + *paddr = ((addr_t *)fp)[-1]; } return 0; } diff --git a/tests/Makefile b/tests/Makefile index 2d6f64fb..54f7c8b2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -14,23 +14,20 @@ TESTS = \ libtest \ libtest_mt \ test3 \ - memtest \ - dlltest \ abitest \ asm-c-connect-test \ vla_test-run \ - cross-test \ tests2-dir \ - pp-dir + pp-dir \ + memtest \ + dlltest \ + cross-test # test4_static -- Not all relocation types are implemented yet. # asmtest / asmtest2 -- minor differences with gcc ifneq ($(CONFIG_bcheck),no) - TESTS += btest test1b - ifndef CONFIG_WIN32 - TESTS += tccb - endif + TESTS += btest test1b tccb endif ifeq ($(CONFIG_dll),no) TESTS := $(filter-out dlltest, $(TESTS)) @@ -82,7 +79,9 @@ all test : @echo ------------ version ------------ @$(TCC_LOCAL) -v @$(MAKE) --no-print-directory -s clean - @$(MAKE) --no-print-directory -s -r $(TESTS) + @$(MAKE) --no-print-directory -s -r _all + +_all : $(TESTS) hello-exe: ../examples/ex1.c @echo ------------ $@ ------------ @@ -199,14 +198,15 @@ btest: boundtest.c echo "Test $$i failed as expected" ; \ fi ;\ done ;\ - echo Bound test OK + echo Bound-Test OK tccb: @echo ------------ $@ ------------ - $(TCC) -b $(TOPSRC)/tcc.c $(TCCFLAGS) $(NATIVE_DEFINES) -o v1-tcc$(EXESUF) - mv v1-tcc$(EXESUF) v2-tcc$(EXESUF) - ./v2-tcc$(EXESUF) -b $(TOPSRC)/tcc.c $(TCCFLAGS) $(NATIVE_DEFINES) -o v1-tcc$(EXESUF) - cmp -s v1-tcc$(EXESUF) v2-tcc$(EXESUF) && echo "Bound-Test tcc OK" + $(TCC) -b $(TOPSRC)/tcc.c $(TCCFLAGS) $(NATIVE_DEFINES) -o tccb1.exe + mv tccb1.exe tccb2.exe + ./tccb2.exe -b $(TOPSRC)/tcc.c $(TCCFLAGS) $(NATIVE_DEFINES) -o tccb1.exe + cmp -s tccb1.exe tccb2.exe && echo "Exe Bound-Rest OK" + # speed test speedtest: ex2 ex3 diff --git a/tests/libtcc_test_mt.c b/tests/libtcc_test_mt.c index 8af4e64c..6c712c61 100644 --- a/tests/libtcc_test_mt.c +++ b/tests/libtcc_test_mt.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "libtcc.h" #define M 20 /* number of states */ @@ -84,6 +85,8 @@ PROG(my_program) "\n" "int foo(int n)\n" "{\n" +" if (n >= N_CRASH && n < N_CRASH + 3)\n" +" *(void**)0 = 0;\n" " printf(\" %d\", fib(n));\n" " return 0;\n" "# warning is this the correct file:line...\n" @@ -110,6 +113,11 @@ void parse_args(TCCState *s) } } +void bt_func(void *pc, const char *file, int line, const char *func) +{ + printf(" *** at %s:%d in '%s'\n", file, line, func); +} + TCCState *new_state(int w) { TCCState *s = tcc_new(); @@ -119,7 +127,14 @@ TCCState *new_state(int w) } tcc_set_error_func(s, stdout, handle_error); parse_args(s); - if (!w) tcc_set_options(s, "-w"); + if (0 == (w & 1)) + tcc_set_options(s, "-w"); + if (w & 2) { + tcc_set_options(s, "-bt"); + tcc_define_symbol(s, "N_CRASH", str(M/2)); + tcc_set_backtrace_func(s, bt_func); + } else + tcc_define_symbol(s, "N_CRASH", "99"); tcc_set_output_type(s, TCC_OUTPUT_MEMORY); return s; } @@ -139,23 +154,28 @@ void *reloc_state(TCCState *s, const char *entry) } /* work with several states at the same time */ -int state_test(void) +int state_test(int w) { TCCState *s[M]; - int (*func[M])(int); + int (*funcs[M])(int); int n; + jmp_buf jb; for (n = 0; n < M + 4; ++n) { unsigned a = n, b = n - 1, c = n - 2, d = n - 3, e = n - 4; if (a < M) - s[a] = new_state(0); + s[a] = new_state(w); if (b < M) if (tcc_compile_string(s[b], my_program) == -1) break; if (c < M) - func[c] = reloc_state(s[c], "foo"); - if (d < M && func[d]) - func[d](F(d)); + funcs[c] = reloc_state(s[c], "foo"); + if (d < M && funcs[d]) { + if ((w & 2) && d == 8) + printf("\n"); + if (0 == tcc_setjmp(s[d], jb, funcs[d])) + funcs[d](F(d)); + } if (e < M) tcc_delete(s[e]); } @@ -257,7 +277,13 @@ int main(int argc, char **argv) #if 1 printf("running fib with mixed calls\n "), fflush(stdout); t = getclock_ms(); - state_test(); + state_test(0); + printf("\n (%u ms)\n", getclock_ms() - t); +#endif +#if 1 + printf("producing some exceptions\n "), fflush(stdout); + t = getclock_ms(); + state_test(2); printf("\n (%u ms)\n", getclock_ms() - t); #endif #if 1 diff --git a/tests/tests2/128_run_atexit.c b/tests/tests2/128_run_atexit.c index 0748c86f..3c7cfbb8 100644 --- a/tests/tests2/128_run_atexit.c +++ b/tests/tests2/128_run_atexit.c @@ -4,6 +4,11 @@ int atexit(void (*function)(void)); int on_exit(void (*function)(int, void *), void *arg); void exit(int status); +void __attribute((constructor)) startup5(void) +{ + printf ("startup5\n"); +} + void cleanup1(void) { printf ("cleanup1\n"); diff --git a/tests/tests2/128_run_atexit.expect b/tests/tests2/128_run_atexit.expect index 3305785f..fda764ce 100644 --- a/tests/tests2/128_run_atexit.expect +++ b/tests/tests2/128_run_atexit.expect @@ -1,4 +1,5 @@ [test_128_return] +startup5 cleanup5 1 cleanup4 1 cleanup3 @@ -7,6 +8,7 @@ cleanup1 [returns 1] [test_128_exit] +startup5 cleanup5 2 cleanup4 2 cleanup3 diff --git a/win32/lib/crt1.c b/win32/lib/crt1.c index 0686302c..6406ed36 100644 --- a/win32/lib/crt1.c +++ b/win32/lib/crt1.c @@ -37,15 +37,6 @@ extern int _tmain(int argc, _TCHAR * argv[], _TCHAR * env[]); #include "crtinit.c" -static int do_main (int argc, _TCHAR * argv[], _TCHAR * env[]) -{ - int retval; - run_ctors(argc, argv, env); - retval = _tmain(__argc, __targv, _tenviron); - run_dtors(); - return retval; -} - /* Allow command-line globbing with "int _dowildcard = 1;" in the user source */ int _dowildcard; @@ -56,6 +47,8 @@ static LONG WINAPI catch_sig(EXCEPTION_POINTERS *ex) void _tstart(void) { + int ret; + _startupinfo start_info = {0}; SetUnhandledExceptionFilter(catch_sig); // Sets the current application type @@ -68,11 +61,21 @@ void _tstart(void) #endif __tgetmainargs( &__argc, &__targv, &_tenviron, _dowildcard, &start_info); - exit(do_main(__argc, __targv, _tenviron)); + run_ctors(__argc, __targv, _tenviron); + ret = _tmain(__argc, __targv, _tenviron); + run_dtors(); + exit(ret); } +// ============================================= +// for 'tcc -run ,,,' + +__attribute__((weak)) extern int __rt_nr_exit; +__attribute__((weak)) extern int __run_on_exit(); + int _runtmain(int argc, /* as tcc passed in */ char **argv) { + int ret; #ifdef UNICODE _startupinfo start_info = {0}; @@ -89,7 +92,12 @@ int _runtmain(int argc, /* as tcc passed in */ char **argv) #if defined __i386__ || defined __x86_64__ _controlfp(_PC_53, _MCW_PC); #endif - return _tmain(__argc, __targv, _tenviron); + __rt_nr_exit = 0; + run_ctors(__argc, __targv, _tenviron); + ret = _tmain(__argc, __targv, _tenviron); + run_dtors(); + __run_on_exit(ret); + return ret; } // =============================================