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)
This commit is contained in:
grischka 2024-02-14 10:00:22 +01:00
parent 8d8d75ca75
commit c88b19966c
16 changed files with 395 additions and 210 deletions

View File

@ -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

View File

@ -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:"

View File

@ -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))

View File

@ -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. */

View File

@ -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";

87
lib/runmain.c Normal file
View File

@ -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

View File

@ -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

4
tcc.h
View File

@ -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

View File

@ -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;
}

View File

@ -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

334
tccrun.c
View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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

View File

@ -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;
}
// =============================================