summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrischka <grischka>2024-02-14 10:00:22 +0100
committergrischka <grischka>2024-02-15 18:45:49 +0100
commitc88b19966c61bfeeeb9b9b092582496875f3d7f5 (patch)
tree55735837ec4d95b9944ca60a3d7ecab95acf91fa
parent8d8d75ca75bc8d2dfd4ff870e9276811a29b9da3 (diff)
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)
-rw-r--r--.github/workflows/build.yml6
-rw-r--r--Makefile28
-rw-r--r--lib/Makefile3
-rw-r--r--lib/bt-exe.c12
-rw-r--r--lib/bt-log.c9
-rw-r--r--lib/runmain.c87
-rw-r--r--libtcc.h11
-rw-r--r--tcc.h4
-rw-r--r--tccelf.c2
-rw-r--r--tccpe.c2
-rw-r--r--tccrun.c312
-rw-r--r--tests/Makefile28
-rw-r--r--tests/libtcc_test_mt.c42
-rw-r--r--tests/tests2/128_run_atexit.c5
-rw-r--r--tests/tests2/128_run_atexit.expect2
-rw-r--r--win32/lib/crt1.c30
16 files changed, 384 insertions, 199 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5fda5d9..e5a8e76 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 652bf2d..98f01e4 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 eba3a6b..1a23223 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 2960dd0..447eaf9 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 05f7c40..87e411d 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 0000000..5ebeeec
--- /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 205ed15..7e032d8 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 837dc6e..16f316d 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 2ccf981..049d674 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 936f146..d2a2c8b 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 54aa9d6..6ba61a6 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;
+ *paddr = rc->ip;
} else {
- ip = 0;
- fp = rc->fp;
- while (--level) {
- /* XXX: check address validity with program info */
- if (fp <= 0x1000)
+ addr_t fp = rc->fp;
+ while (1) {
+ if (fp < 0x1000)
+ return -1;
+ if (0 == --level)
break;
+ /* XXX: check address validity with program info */
fp = ((addr_t *)fp)[0];
}
- if (fp > 0x1000)
- ip = ((addr_t *)fp)[1];
+ *paddr = ((addr_t *)fp)[1];
}
- if (ip <= 0x1000)
- return -1;
- *paddr = ip;
return 0;
}
-#elif defined(__arm__)
+/* XXX: only supports linux/bsd */
+#elif defined(__arm__) && !defined(_WIN32)
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;
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 2d6f64f..54f7c8b 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 8af4e64..6c712c6 100644
--- a/tests/libtcc_test_mt.c
+++ b/tests/libtcc_test_mt.c
@@ -6,6 +6,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <setjmp.h>
#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 0748c86..3c7cfbb 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 3305785..fda764c 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 0686302..6406ed3 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;
}
// =============================================