tccrun.c: standalone backtraces with -bt[N] or -b

This makes it possible to get backtraces with executables
(including DLLs/SOs) like we had it already with -g -run.

Option -b includes -bt, and -bt includes -g.

- new file lib/bt-exe.c: used to link rt_printline and the
  exception handler from tccrun.c into executables/DLLs.

- new file lib/bt-log.c: provides a function that may be
  called from user code to print out a backtrace with a
  message (currently for i386/x86_64 only):

     int (*tcc_backtrace)(const char *fmt, ...);

  As an extra hack, if 'fmt' is prefixed like "^file.c^..."
  then the backtrace will skip calls from within 'file.c'.

- new file lib/bt-dll.c:  used on win32 to link the backtrace
  and bcheck functions with the main module at runtime

- bcheck.c: now uses the tcc_backtrace function from above

- tccgen.c: minor cleanups

- tccelf.c: stab sections get SHF_ALLOC for easy access.
  Also in relocate_section(): 64bit relocations for stabs
  in DLLs cannot work.  To find DLL addresses, the DLL base
  is added manually in tccrun.c via rc.prog_base instead.

- tccpe.c: there are some changes to allow merging sections,
  used to merge .finit_array into .data in the first place.

- tccpp.c: tcc -run now #defines __TCC_RUN__
  also: refactor a line in tal_realloc that was incompatible
  with bcheck

- tcctest.c: fixed a problem with r12 which tcc cannot preserve
  as well as gcc does.

- tests2/112_backtrace.c: test the feature and the bcheck test18
  that previously was in boundtest.c
This commit is contained in:
gr
2020-01-17 22:58:39 +01:00
parent 4092b05068
commit ef42295fe8
27 changed files with 1216 additions and 855 deletions

View File

@@ -13,7 +13,7 @@ XCC = $(XTCC)
XAR = $(XTCC) -ar
XFLAGS-unx = -B$(TOPSRC)
XFLAGS-win = -B$(TOPSRC)/win32 -I$(TOPSRC)/include
XFLAGS = $(XFLAGS$(XCFG))
XFLAGS = $(XFLAGS$(XCFG)) -I$(TOP)
XCFG = $(or $(findstring -win,$T),-unx)
# in order to use gcc, tyoe: make <target>-libtcc1-usegcc=yes
@@ -27,6 +27,8 @@ endif
# only for native compiler
$(X)BCHECK_O = bcheck.o
$(X)BT_O = bt-exe.o bt-log.o
$(X)B_O = bcheck.o bt-exe.o bt-log.o bt-dll.o
ifeq ($(CONFIG_musl)$(CONFIG_uClibc),yes)
BCHECK_O =
@@ -38,18 +40,18 @@ ifdef CONFIG_OSX
XFLAGS += -D_ANSI_SOURCE
endif
I386_O = libtcc1.o alloca86.o alloca86-bt.o
X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o
ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o
ARM64_O = lib-arm64.o
RISCV64_O = lib-arm64.o
I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BT_O)
X86_64_O = libtcc1.o alloca86_64.o alloca86_64-bt.o $(BT_O)
ARM_O = libtcc1.o armeabi.o alloca-arm.o armflush.o $(BT_O)
ARM64_O = lib-arm64.o $(BT_O)
RISCV64_O = lib-arm64.o $(BT_O)
WIN_O = crt1.o crt1w.o wincrt1.o wincrt1w.o dllcrt1.o dllmain.o
OBJ-i386 = $(I386_O) $(BCHECK_O) $(DSO_O)
OBJ-x86_64 = $(X86_64_O) va_list.o $(BCHECK_O) $(DSO_O)
OBJ-x86_64-osx = $(X86_64_O) va_list.o
OBJ-i386-win32 = $(I386_O) chkstk.o bcheck.o $(WIN_O)
OBJ-x86_64-win32 = $(X86_64_O) chkstk.o bcheck.o $(WIN_O)
OBJ-i386-win32 = $(I386_O) chkstk.o $(B_O) $(WIN_O)
OBJ-x86_64-win32 = $(X86_64_O) chkstk.o $(B_O) $(WIN_O)
OBJ-arm64 = $(ARM64_O) $(DSO_O)
OBJ-arm = $(ARM_O) $(DSO_O)
OBJ-arm-fpa = $(ARM_O) $(DSO_O)
@@ -60,7 +62,7 @@ OBJ-arm-eabihf = $(ARM_O) $(DSO_O)
OBJ-arm-wince = $(ARM_O) $(WIN_O)
OBJ-riscv64 = $(RISCV64_O) $(DSO_O)
OBJ-extra = $(filter bcheck.o,$(OBJ-$T))
OBJ-extra = $(filter $(B_O),$(OBJ-$T))
OBJ-libtcc1 = $(addprefix $(X),$(filter-out $(OBJ-extra),$(OBJ-$T)))
ALL = $(addprefix $(TOP)/,$(X)libtcc1.a $(OBJ-extra))
@@ -80,8 +82,10 @@ $(TOP)/%.o : %.c
$(XCC) -c $< -o $@ $(XFLAGS)
$(TOP)/bcheck.o : XFLAGS += -g
$(TOP)/bt-exe.o : $(TOP)/tccrun.c
$(X)crt1w.o : crt1.c
$(X)wincrt1w.o : wincrt1.c
clean :
rm -f *.a *.o $(ALL)

View File

@@ -49,6 +49,12 @@
#endif
#define FASTCALL __attribute__((regparm(3)))
#ifdef _WIN32
# define DLL_EXPORT __declspec(dllexport)
#else
# define DLL_EXPORT
#endif
#if defined(__FreeBSD__) \
|| defined(__FreeBSD_kernel__) \
|| defined(__DragonFly__) \
@@ -162,16 +168,16 @@ void splay_printtree(Tree * t, int d);
/* external interface */
void __bound_checking (int no_check);
void __bound_never_fatal (int no_check);
void * __bound_ptr_add(void *p, size_t offset);
void * __bound_ptr_indir1(void *p, size_t offset);
void * __bound_ptr_indir2(void *p, size_t offset);
void * __bound_ptr_indir4(void *p, size_t offset);
void * __bound_ptr_indir8(void *p, size_t offset);
void * __bound_ptr_indir12(void *p, size_t offset);
void * __bound_ptr_indir16(void *p, size_t offset);
void FASTCALL __bound_local_new(void *p1);
void FASTCALL __bound_local_delete(void *p1);
void __bound_init(void);
DLL_EXPORT void * __bound_ptr_add(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir1(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir2(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir4(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir8(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir12(void *p, size_t offset);
DLL_EXPORT void * __bound_ptr_indir16(void *p, size_t offset);
DLL_EXPORT void FASTCALL __bound_local_new(void *p1);
DLL_EXPORT void FASTCALL __bound_local_delete(void *p1);
void __bound_init(size_t *);
void __bound_main_arg(char **p);
void __bound_exit(void);
#if !defined(_WIN32)
@@ -179,35 +185,32 @@ void *__bound_mmap (void *start, size_t size, int prot, int flags, int fd,
off_t offset);
int __bound_munmap (void *start, size_t size);
#endif
void __bound_new_region(void *p, size_t size);
void *__bound_memcpy(void *dst, const void *src, size_t size);
int __bound_memcmp(const void *s1, const void *s2, size_t size);
void *__bound_memmove(void *dst, const void *src, size_t size);
void *__bound_memset(void *dst, int c, size_t size);
int __bound_strlen(const char *s);
char *__bound_strcpy(char *dst, const char *src);
char *__bound_strncpy(char *dst, const char *src, size_t n);
int __bound_strcmp(const char *s1, const char *s2);
int __bound_strncmp(const char *s1, const char *s2, size_t n);
char *__bound_strcat(char *dest, const char *src);
char *__bound_strchr(const char *string, int ch);
char *__bound_strdup(const char *s);
DLL_EXPORT void __bound_new_region(void *p, size_t size);
DLL_EXPORT void *__bound_memcpy(void *dst, const void *src, size_t size);
DLL_EXPORT int __bound_memcmp(const void *s1, const void *s2, size_t size);
DLL_EXPORT void *__bound_memmove(void *dst, const void *src, size_t size);
DLL_EXPORT void *__bound_memset(void *dst, int c, size_t size);
DLL_EXPORT int __bound_strlen(const char *s);
DLL_EXPORT char *__bound_strcpy(char *dst, const char *src);
DLL_EXPORT char *__bound_strncpy(char *dst, const char *src, size_t n);
DLL_EXPORT int __bound_strcmp(const char *s1, const char *s2);
DLL_EXPORT int __bound_strncmp(const char *s1, const char *s2, size_t n);
DLL_EXPORT char *__bound_strcat(char *dest, const char *src);
DLL_EXPORT char *__bound_strchr(const char *string, int ch);
DLL_EXPORT char *__bound_strdup(const char *s);
#if !MALLOC_REDIR
void *__bound_malloc(size_t size, const void *caller);
void *__bound_memalign(size_t size, size_t align, const void *caller);
void __bound_free(void *ptr, const void *caller);
void *__bound_realloc(void *ptr, size_t size, const void *caller);
void *__bound_calloc(size_t nmemb, size_t size);
DLL_EXPORT void *__bound_malloc(size_t size, const void *caller);
DLL_EXPORT void *__bound_memalign(size_t size, size_t align, const void *caller);
DLL_EXPORT void __bound_free(void *ptr, const void *caller);
DLL_EXPORT void *__bound_realloc(void *ptr, size_t size, const void *caller);
DLL_EXPORT void *__bound_calloc(size_t nmemb, size_t size);
#endif
#define FREE_REUSE_SIZE (100)
static unsigned int free_reuse_index;
static void *free_reuse_list[FREE_REUSE_SIZE];
/* error message, just for TCC */
const char *__bound_error_msg;
static Tree *tree = NULL;
#define TREE_REUSE (1)
#if TREE_REUSE
@@ -296,14 +299,18 @@ void __bound_never_fatal (int neverfatal)
fetch_and_add (&never_fatal, neverfatal);
}
int tcc_backtrace(const char *fmt, ...);
/* print a bound error message */
static void bound_error(const char *error)
{
__bound_error_msg = error;
fprintf(stderr,"%s%s %s: %s\n", exec, __FILE__, __FUNCTION__, error);
if (never_fatal == 0)
*(void **)0 = 0; /* force a runtime error */
}
#define bound_warning(...) \
tcc_backtrace("^bcheck.c^BCHECK: " __VA_ARGS__)
#define bound_error(...) \
do { \
bound_warning(__VA_ARGS__); \
if (never_fatal == 0) \
exit(255); \
} while (0)
static void bound_alloc_error(const char *s)
{
@@ -317,20 +324,6 @@ static void bound_not_found_warning(const char *file, const char *function,
dprintf(stderr, "%s%s, %s(): Not found %p\n", exec, file, function, ptr);
}
static void bound_ptr_add_warning(const char *file, const char *function,
void *ptr)
{
fprintf(stderr,"%s%s %s(): %p is outside of the region\n",
exec, file, function, ptr);
}
static void bound_ptr_indir_error(const char *file, const char *function,
void *ptr)
{
fprintf(stderr,"%s%s %s(): %p is outside of the region\n",
exec, file, function, ptr);
}
/* return '(p + offset)' for pointer arithmetic (a pointer can reach
the end of a region in this case */
void * __bound_ptr_add(void *p, size_t offset)
@@ -360,8 +353,7 @@ void * __bound_ptr_add(void *p, size_t offset)
if (addr <= tree->size) {
if (tree->is_invalid || addr + offset > tree->size) {
POST_SEM ();
if (print_warn_ptr_add)
bound_ptr_add_warning (__FILE__, __FUNCTION__, p + offset);
bound_warning("%p is outside of the region", p + offset);
if (never_fatal <= 0)
return INVALID_POINTER; /* return an invalid pointer */
return p + offset;
@@ -407,8 +399,7 @@ void * __bound_ptr_indir ## dsize (void *p, size_t offset) \
if (addr <= tree->size) { \
if (tree->is_invalid || addr + offset + dsize > tree->size) { \
POST_SEM (); \
bound_ptr_indir_error (__FILE__, __FUNCTION__, \
p + offset); \
bound_warning("%p is outside of the region", p + offset); \
if (never_fatal <= 0) \
return INVALID_POINTER; /* return an invalid pointer */ \
return p + offset; \
@@ -460,8 +451,7 @@ void FASTCALL __bound_local_new(void *p1)
WAIT_SEM ();
while ((addr = p[0])) {
INCR_COUNT(bound_local_new_count);
if (addr != 1)
tree = splay_insert(addr + fp, p[1], tree);
tree = splay_insert(addr + fp, p[1], tree);
p += 2;
}
POST_SEM ();
@@ -498,7 +488,10 @@ void FASTCALL __bound_local_delete(void *p1)
WAIT_SEM ();
while ((addr = p[0])) {
INCR_COUNT(bound_local_delete_count);
if (addr == 1) {
tree = splay_delete(addr + fp, tree);
p += 2;
}
{
alloca_list_type *last = NULL;
alloca_list_type *cur = alloca_list;
@@ -518,11 +511,8 @@ void FASTCALL __bound_local_delete(void *p1)
cur = cur->next;
}
}
}
else
tree = splay_delete(addr + fp, tree);
p += 2;
}
POST_SEM ();
while (free_list) {
alloca_list_type *next = free_list->next;
@@ -607,14 +597,14 @@ void __bound_new_region(void *p, size_t size)
#pragma GCC diagnostic pop
#endif
void __attribute__((constructor)) __bound_init(void)
void __bound_init(size_t *p)
{
extern size_t __bounds_start[];
size_t *p;
if (inited)
return;
dprintf(stderr, "%s, %s(): start\n", __FILE__, __FUNCTION__);
if (inited) {
WAIT_SEM();
goto add_bounds;
}
inited = 1;
print_warn_ptr_add = getenv ("TCC_BOUNDS_WARN_POINTER_ADD") != NULL;
@@ -623,8 +613,6 @@ void __attribute__((constructor)) __bound_init(void)
print_statistic = getenv ("TCC_BOUNDS_PRINT_STATISTIC") != NULL;
never_fatal = getenv ("TCC_BOUNDS_NEVER_FATAL") != NULL;
dprintf(stderr, "%s, %s(): start\n", __FILE__, __FUNCTION__);
INIT_SEM ();
#if MALLOC_REDIR
@@ -724,27 +712,26 @@ void __attribute__((constructor)) __bound_init(void)
tree = splay_insert((size_t) (&errno), sizeof (int), tree);
#endif
add_bounds:
if (!p)
goto no_bounds;
/* add all static bound check values */
p = __bounds_start;
while (p[0] != 0) {
tree = splay_insert(p[0], p[1], tree);
p += 2;
}
POST_SEM ();
#if BOUND_DEBUG
if (print_calls) {
p = __bounds_start;
while (p[0] != 0) {
if (print_calls) {
dprintf(stderr, "%s, %s(): static var %p 0x%lx\n",
__FILE__, __FUNCTION__,
(void *) p[0], (unsigned long) p[1]);
p += 2;
}
}
#endif
p += 2;
}
no_bounds:
POST_SEM ();
no_checking = 0;
dprintf(stderr, "%s, %s(): end\n\n", __FILE__, __FUNCTION__);
}
@@ -931,7 +918,7 @@ void *__bound_malloc(size_t size, const void *caller)
#if MALLOC_REDIR
/* This will catch the first dlsym call from __bound_init */
if (malloc_redir == NULL) {
__bound_init ();
__bound_init (0);
if (malloc_redir == NULL) {
ptr = &initial_pool[pool_index];
pool_index = (pool_index + size + 15) & ~15;
@@ -1113,7 +1100,7 @@ void *__bound_calloc(size_t nmemb, size_t size)
#if MALLOC_REDIR
/* This will catch the first dlsym call from __bound_init */
if (malloc_redir == NULL) {
__bound_init ();
__bound_init (0);
if (malloc_redir == NULL) {
ptr = &initial_pool[pool_index];
pool_index = (pool_index + size + 15) & ~15;
@@ -1190,10 +1177,8 @@ static void __bound_check(const void *p, size_t size, const char *function)
{
if (no_checking == 0 && size != 0 &&
__bound_ptr_add((void *)p, size) == INVALID_POINTER) {
static char line[100];
sprintf(line, "invalid pointer %p, size 0x%lx in %s",
bound_error("invalid pointer %p, size 0x%lx in %s",
p, (unsigned long)size, function);
bound_error(line);
}
}
@@ -1207,10 +1192,8 @@ static int check_overlap (const void *p1, size_t n1,
if (no_checking == 0 && n1 != 0 && n2 !=0 &&
((p1 <= p2 && p1e > p2) || /* p1----p2====p1e----p2e */
(p2 <= p1 && p2e > p1))) { /* p2----p1====p2e----p1e */
static char line[100];
sprintf(line, "overlapping regions %p(0x%lx), %p(0x%lx) in %s",
bound_error("overlapping regions %p(0x%lx), %p(0x%lx) in %s",
p1, (unsigned long)n1, p2, (unsigned long)n2, function);
bound_error(line);
return never_fatal < 0;
}
return 0;

67
lib/bt-dll.c Normal file
View File

@@ -0,0 +1,67 @@
/* ------------------------------------------------------------- */
/* stubs for calling bcheck functions from a dll. */
#include <windows.h>
#include <stdio.h>
#define REDIR_ALL \
REDIR(__bt_init) \
REDIR(tcc_backtrace) \
\
REDIR(__bound_ptr_add) \
REDIR(__bound_ptr_indir1) \
REDIR(__bound_ptr_indir2) \
REDIR(__bound_ptr_indir4) \
REDIR(__bound_ptr_indir8) \
REDIR(__bound_ptr_indir12) \
REDIR(__bound_ptr_indir16) \
REDIR(__bound_local_new) \
REDIR(__bound_local_delete) \
REDIR(__bound_new_region) \
\
REDIR(__bound_free) \
REDIR(__bound_malloc) \
REDIR(__bound_realloc) \
REDIR(__bound_memcpy) \
REDIR(__bound_memcmp) \
REDIR(__bound_memmove) \
REDIR(__bound_memset) \
REDIR(__bound_strlen) \
REDIR(__bound_strcpy) \
REDIR(__bound_strncpy) \
REDIR(__bound_strcmp) \
REDIR(__bound_strncmp) \
REDIR(__bound_strcat) \
REDIR(__bound_strchr) \
REDIR(__bound_strdup)
#define REDIR(s) void *s;
static struct { REDIR_ALL } all_ptrs;
#undef REDIR
#define REDIR(s) #s"\0"
static const char all_names[] = REDIR_ALL;
#undef REDIR
#define REDIR(s) __asm__(".global "#s";"#s": jmp *%0" : : "m" (all_ptrs.s) );
static void all_jmps() { REDIR_ALL }
#undef REDIR
void __bt_init_dll(int bcheck)
{
const char *s = all_names;
void **p = (void**)&all_ptrs;
do {
*p = (void*)GetProcAddress(GetModuleHandle(NULL), (char*)s);
if (NULL == *p) {
char buf[100];
sprintf(buf,
"Error: function '%s()' not found in executable. "
"(Need -bt or -b for linking the exe.)", s);
if (GetStdHandle(STD_ERROR_HANDLE))
fprintf(stderr, "TCC/BCHECK: %s\n", buf), fflush(stderr);
else
MessageBox(NULL, buf, "TCC/BCHECK", MB_ICONERROR);
ExitProcess(1);
}
s = strchr(s,'\0') + 1, ++p;
} while (*s && (bcheck || p < &all_ptrs.__bound_ptr_add));
}

43
lib/bt-exe.c Normal file
View File

@@ -0,0 +1,43 @@
/* ------------------------------------------------------------- */
/* for linking rt_printline and the signal/exception handler
from tccrun.c into executables. */
#define CONFIG_TCC_BACKTRACE_ONLY
#include "../tccrun.c"
int (*__rt_error)(void*, void*, const char *, va_list);
#ifndef _WIN32
# define __declspec(n)
#endif
__declspec(dllexport)
void __bt_init(rt_context *p, int num_callers)
{
__attribute__((weak)) int main();
__attribute__((weak)) void __bound_init(void*);
struct rt_context *rc = &g_rtctxt;
//fprintf(stderr, "__bt_init %d %p %p\n", num_callers, p->stab_sym, p->bounds_start), fflush(stderr);
if (num_callers) {
memcpy(rc, p, offsetof(rt_context, next));
rc->num_callers = num_callers - 1;
rc->top_func = main;
__rt_error = _rt_error;
set_exception_handler();
} else {
p->next = rc->next, rc->next = p;
}
if (__bound_init && p->bounds_start)
__bound_init(p->bounds_start);
}
/* copy a string and truncate it. */
static char *pstrcpy(char *buf, size_t buf_size, const char *s)
{
int l = strlen(s);
if (l >= buf_size)
l = buf_size - 1;
memcpy(buf, s, l);
buf[l] = 0;
return buf;
}

36
lib/bt-log.c Normal file
View File

@@ -0,0 +1,36 @@
/* ------------------------------------------------------------- */
/* function to get a stack backtrace on demand with a message */
#include <stdio.h>
#include <string.h>
int (*__rt_error)(void*, void*, const char *, va_list);
#ifdef _WIN32
# define DLL_EXPORT __declspec(dllexport)
#else
# define DLL_EXPORT
#endif
DLL_EXPORT int tcc_backtrace(const char *fmt, ...)
{
va_list ap;
int ret;
if (__rt_error) {
void *fp = __builtin_frame_address(1);
void *ip = __builtin_return_address(0);
va_start(ap, fmt);
ret = __rt_error(fp, ip, fmt, ap);
va_end(ap);
} else {
const char *p;
if (fmt[0] == '^' && (p = strchr(fmt + 1, fmt[0])))
fmt = p + 1;
va_start(ap, fmt);
ret = vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n"), fflush(stderr);
}
return ret;
}