diff options
Diffstat (limited to 'tinycc/tccrun.c')
| -rw-r--r-- | tinycc/tccrun.c | 1461 |
1 files changed, 0 insertions, 1461 deletions
diff --git a/tinycc/tccrun.c b/tinycc/tccrun.c deleted file mode 100644 index d373abc..0000000 --- a/tinycc/tccrun.c +++ /dev/null @@ -1,1461 +0,0 @@ -/* - * TCC - Tiny C Compiler - Support for -run switch - * - * Copyright (c) 2001-2004 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "tcc.h" - -/* only native compiler supports -run */ -#ifdef TCC_IS_NATIVE - -#ifdef CONFIG_TCC_BACKTRACE -typedef struct rt_context -{ - /* --> tccelf.c:tcc_add_btstub wants those below in that order: */ - union { - struct { - Stab_Sym *stab_sym, *stab_sym_end; - char *stab_str; - }; - struct { - unsigned char *dwarf_line, *dwarf_line_end, *dwarf_line_str; - }; - }; - addr_t dwarf; - ElfW(Sym) *esym_start, *esym_end; - char *elf_str; - addr_t prog_base; - void *bounds_start; - struct rt_context *next; - /* <-- */ - int num_callers; - addr_t ip, fp, sp; - void *top_func; - jmp_buf jmp_buf; - char do_jmp; -} rt_context; - -static rt_context g_rtctxt; -static void set_exception_handler(void); -static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap); -static void rt_exit(int code); -#endif /* CONFIG_TCC_BACKTRACE */ - -/* defined when included from lib/bt-exe.c */ -#ifndef CONFIG_TCC_BACKTRACE_ONLY - -#ifndef _WIN32 -# include <sys/mman.h> -#endif - -static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length); -static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff); - -#ifdef _WIN64 -static void *win64_add_function_table(TCCState *s1); -static void win64_del_function_table(void *); -#endif - -/* ------------------------------------------------------------- */ -/* Do all relocations (needed before using tcc_get_symbol()) - Returns -1 on error. */ - -LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) -{ - int size; - addr_t ptr_diff = 0; - - if (TCC_RELOCATE_AUTO != ptr) - return tcc_relocate_ex(s1, ptr, 0); - - size = tcc_relocate_ex(s1, NULL, 0); - if (size < 0) - return -1; - -#ifdef HAVE_SELINUX -{ - /* Using mmap instead of malloc */ - void *prx; - char tmpfname[] = "/tmp/.tccrunXXXXXX"; - int fd = mkstemp(tmpfname); - unlink(tmpfname); - ftruncate(fd, size); - - size = (size + (PAGESIZE-1)) & ~(PAGESIZE-1); - ptr = mmap(NULL, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - /* mmap RX memory at a fixed distance */ - prx = mmap((char*)ptr + size, size, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_FIXED, fd, 0); - close(fd); - if (ptr == MAP_FAILED || prx == MAP_FAILED) - return tcc_error_noabort("tccrun: could not map memory"); - ptr_diff = (char*)prx - (char*)ptr; - //printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff); -} -#else - ptr = tcc_malloc(size); -#endif - if (tcc_relocate_ex(s1, ptr, ptr_diff)) - return -1; - dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size); - dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr); - return 0; -} - -ST_FUNC void tcc_run_free(TCCState *s1) -{ - int i; - - for (i = 0; i < s1->nb_runtime_mem; i += 2) { - unsigned size = (unsigned)(addr_t)s1->runtime_mem[i]; - void *ptr = s1->runtime_mem[i+1]; -#ifdef HAVE_SELINUX - munmap(ptr, size * 2); -#else - /* unprotect memory to make it usable for malloc again */ - set_pages_executable(s1, 2, ptr, size); -#ifdef _WIN64 - win64_del_function_table(*(void**)ptr); -#endif - tcc_free(ptr); -#endif - } - tcc_free(s1->runtime_mem); -} - -static void run_cdtors(TCCState *s1, const char *start, const char *end, - int argc, char **argv, char **envp) -{ - 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); -} - -#define NR_AT_EXIT 32 - -static struct exit_context { - int exit_called; - int nr_exit; - void (*exitfunc[NR_AT_EXIT])(int, void *); - void *exitarg[NR_AT_EXIT]; -#ifndef CONFIG_TCC_BACKTRACE - jmp_buf run_jmp_buf; -#endif -} g_exit_context; - -static void init_exit(void) -{ - struct exit_context *e = &g_exit_context; - - e->exit_called = 0; - e->nr_exit = 0; -} - -static void call_exit(int ret) -{ - struct exit_context *e = &g_exit_context; - - while (e->nr_exit) { - e->nr_exit--; - e->exitfunc[e->nr_exit](ret, e->exitarg[e->nr_exit]); - } -} - -static int rt_atexit(void (*function)(void)) -{ - struct exit_context *e = &g_exit_context; - - if (e->nr_exit < NR_AT_EXIT) { - e->exitfunc[e->nr_exit] = (void (*)(int, void *))function; - e->exitarg[e->nr_exit++] = NULL; - return 0; - } - return 1; -} - -static int rt_on_exit(void (*function)(int, void *), void *arg) -{ - struct exit_context *e = &g_exit_context; - - if (e->nr_exit < NR_AT_EXIT) { - e->exitfunc[e->nr_exit] = function; - e->exitarg[e->nr_exit++] = arg; - return 0; - } - return 1; -} - -static void run_exit(int code) -{ - struct exit_context *e = &g_exit_context; - - e->exit_called = 1; -#ifdef CONFIG_TCC_BACKTRACE - longjmp((&g_rtctxt)->jmp_buf, code ? code : 256); -#else - longjmp(e->run_jmp_buf, code ? code : 256); -#endif -} - -/* 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; -#ifdef CONFIG_TCC_BACKTRACE - rt_context *rc = &g_rtctxt; -#endif - -#if defined(__APPLE__) || defined(__FreeBSD__) - char **envp = NULL; -#elif defined(__OpenBSD__) || defined(__NetBSD__) - extern char **environ; - char **envp = environ; -#else - char **envp = environ; -#endif - - s1->runtime_main = s1->nostdlib ? "_start" : "main"; - if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1)) - return 0; - tcc_add_symbol(s1, "exit", run_exit); - tcc_add_symbol(s1, "atexit", rt_atexit); - tcc_add_symbol(s1, "on_exit", rt_on_exit); - if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0) - return -1; - prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1); - if ((addr_t)-1 == (addr_t)prog_main) - return -1; - -#ifdef CONFIG_TCC_BACKTRACE - memset(rc, 0, sizeof *rc); - if (s1->do_debug) { - void *p; - if (s1->dwarf) { - rc->dwarf_line = dwarf_line_section->data; - rc->dwarf_line_end = dwarf_line_section->data + dwarf_line_section->data_offset; - if (dwarf_line_str_section) - rc->dwarf_line_str = dwarf_line_str_section->data; - } - else - { - rc->stab_sym = (Stab_Sym *)stab_section->data; - rc->stab_sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset); - rc->stab_str = (char *)stab_section->link->data; - } - rc->dwarf = s1->dwarf; - 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; -#if PTR_SIZE == 8 - rc->prog_base = text_section->sh_addr & 0xffffffff00000000ULL; -#if defined TCC_TARGET_MACHO - if (s1->dwarf) - rc->prog_base = (addr_t) -1; -#else -#endif -#endif - rc->top_func = tcc_get_symbol(s1, "main"); - rc->num_callers = s1->rt_num_callers; - rc->do_jmp = 1; - if ((p = tcc_get_symbol(s1, "__rt_error"))) - *(void**)p = _rt_error; -#ifdef CONFIG_TCC_BCHECK - if (s1->do_bounds_check) { - rc->bounds_start = (void*)bounds_section->sh_addr; - if ((p = tcc_get_symbol(s1, "__bound_init"))) - ((void(*)(void*,int))p)(rc->bounds_start, 1); - } -#endif - set_exception_handler(); - } -#endif - - errno = 0; /* clean errno value */ - fflush(stdout); - fflush(stderr); - init_exit(); - /* These aren't C symbols, so don't need leading underscore handling. */ - run_cdtors(s1, "__init_array_start", "__init_array_end", argc, argv, envp); -#ifdef CONFIG_TCC_BACKTRACE - if (!(ret = setjmp(rc->jmp_buf))) -#else - if (!(ret = setjmp((&g_exit_context)->run_jmp_buf))) -#endif - { - ret = prog_main(argc, argv, envp); - } - run_cdtors(s1, "__fini_array_start", "__fini_array_end", 0, NULL, NULL); - call_exit(ret); - if ((s1->dflag & 16) && ret) - fprintf(s1->ppfp, "[returns %d]\n", ret), fflush(s1->ppfp); - if ((s1->dflag & 16) == 0 && (&g_exit_context)->exit_called) - exit(ret); - return ret; -} - -#define DEBUG_RUNMEN 0 - -/* enable rx/ro/rw permissions */ -#define CONFIG_RUNMEM_RO 1 - -#if CONFIG_RUNMEM_RO -# define PAGE_ALIGN PAGESIZE -#elif defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 -/* To avoid that x86 processors would reload cached instructions - each time when data is written in the near, we need to make - sure that code and data do not share the same 64 byte unit */ -# define PAGE_ALIGN 64 -#else -# define PAGE_ALIGN 1 -#endif - -/* relocate code. Return -1 on error, required size if ptr is NULL, - otherwise copy code into buffer passed by the caller */ -static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff) -{ - Section *s; - unsigned offset, length, align, max_align, i, k, f; - unsigned n, copy; - addr_t mem, addr; - - if (NULL == ptr) { - s1->nb_errors = 0; -#ifdef TCC_TARGET_PE - pe_output_file(s1, NULL); -#else - tcc_add_runtime(s1); - resolve_common_syms(s1); - build_got_entries(s1, 0); -#endif - if (s1->nb_errors) - return -1; - } - - offset = max_align = 0, mem = (addr_t)ptr; -#ifdef _WIN64 - offset += sizeof (void*); /* space for function_table pointer */ -#endif - copy = 0; -redo: - for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */ - n = 0; addr = 0; - for(i = 1; i < s1->nb_sections; i++) { - static const char shf[] = { - SHF_ALLOC|SHF_EXECINSTR, SHF_ALLOC, SHF_ALLOC|SHF_WRITE - }; - s = s1->sections[i]; - if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR))) - continue; - length = s->data_offset; - if (copy) { - if (addr == 0) - addr = s->sh_addr; - n = (s->sh_addr - addr) + length; - ptr = (void*)s->sh_addr; - if (k == 0) - ptr = (void*)(s->sh_addr - ptr_diff); - if (NULL == s->data || s->sh_type == SHT_NOBITS) - memset(ptr, 0, length); - else - memcpy(ptr, s->data, length); -#ifdef _WIN64 - if (s == s1->uw_pdata) - *(void**)mem = win64_add_function_table(s1); -#endif - if (s->data) { - tcc_free(s->data); - s->data = NULL; - s->data_allocated = 0; - } - s->data_offset = 0; - continue; - } - align = s->sh_addralign - 1; - if (++n == 1 && align < (PAGE_ALIGN - 1)) - align = (PAGE_ALIGN - 1); - if (max_align < align) - max_align = align; - addr = k ? mem : mem + ptr_diff; - offset += -(addr + offset) & align; - s->sh_addr = mem ? addr + offset : 0; - offset += length; -#if DEBUG_RUNMEN - if (mem) - printf("%d: %-16s %p len %04x align %04x\n", - k, s->name, (void*)s->sh_addr, length, align + 1); -#endif - } - if (copy) { /* set permissions */ - if (k == 0 && ptr_diff) - continue; /* not with HAVE_SELINUX */ - f = k; -#if !CONFIG_RUNMEM_RO - if (f != 0) - continue; - f = 3; /* change only SHF_EXECINSTR to rwx */ -#endif -#if DEBUG_RUNMEN - printf("protect %d %p %04x\n", f, (void*)addr, n); -#endif - if (n) { - if (set_pages_executable(s1, f, (void*)addr, n)) - return -1; - } - } - } - - if (copy) - return 0; - - /* relocate symbols */ - relocate_syms(s1, s1->symtab, !(s1->nostdlib)); - if (s1->nb_errors) - return -1; - if (0 == mem) - return offset + max_align; - -#ifdef TCC_TARGET_PE - s1->pe_imagebase = mem; -#endif - - /* relocate sections */ -#ifndef TCC_TARGET_PE - relocate_plt(s1); -#endif - relocate_sections(s1); - copy = 1; - goto redo; -} - -/* ------------------------------------------------------------- */ -/* allow to run code in memory */ - -static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length) -{ -#ifdef _WIN32 - static const unsigned char protect[] = { - PAGE_EXECUTE_READ, - PAGE_READONLY, - PAGE_READWRITE, - PAGE_EXECUTE_READWRITE - }; - DWORD old; - if (!VirtualProtect(ptr, length, protect[mode], &old)) - return -1; - return 0; -#else - static const unsigned char protect[] = { - PROT_READ | PROT_EXEC, - PROT_READ, - PROT_READ | PROT_WRITE, - PROT_READ | PROT_WRITE | PROT_EXEC - }; - addr_t start, end; - start = (addr_t)ptr & ~(PAGESIZE - 1); - end = (addr_t)ptr + length; - end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); - if (mprotect((void *)start, end - start, protect[mode])) - return tcc_error_noabort("mprotect failed: did you mean to configure --with-selinux?"); -/* XXX: BSD sometimes dump core with bad system call */ -# if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64 - if (mode == 0 || mode == 3) { - void __clear_cache(void *beginning, void *end); - __clear_cache(ptr, (char *)ptr + length); - } -# endif - return 0; -#endif -} - -#ifdef _WIN64 -static void *win64_add_function_table(TCCState *s1) -{ - void *p = NULL; - if (s1->uw_pdata) { - p = (void*)s1->uw_pdata->sh_addr; - RtlAddFunctionTable( - (RUNTIME_FUNCTION*)p, - s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION), - s1->pe_imagebase - ); - s1->uw_pdata = NULL; - } - return p; -} - -static void win64_del_function_table(void *p) -{ - if (p) { - RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p); - } -} -#endif -#endif //ndef CONFIG_TCC_BACKTRACE_ONLY -/* ------------------------------------------------------------- */ -#ifdef CONFIG_TCC_BACKTRACE - -static int rt_vprintf(const char *fmt, va_list ap) -{ - int ret = vfprintf(stderr, fmt, ap); - fflush(stderr); - return ret; -} - -static int rt_printf(const char *fmt, ...) -{ - va_list ap; - int r; - va_start(ap, fmt); - r = rt_vprintf(fmt, ap); - va_end(ap); - return r; -} - -static char *rt_elfsym(rt_context *rc, addr_t wanted_pc, addr_t *func_addr) -{ - ElfW(Sym) *esym; - for (esym = rc->esym_start + 1; esym < rc->esym_end; ++esym) { - int type = ELFW(ST_TYPE)(esym->st_info); - if ((type == STT_FUNC || type == STT_GNU_IFUNC) - && wanted_pc >= esym->st_value - && wanted_pc < esym->st_value + esym->st_size) { - *func_addr = esym->st_value; - return rc->elf_str + esym->st_name; - } - } - return NULL; -} - - -/* 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) -{ - char func_name[128]; - addr_t func_addr, last_pc, pc; - const char *incl_files[INCLUDE_STACK_SIZE]; - int incl_index, last_incl_index, len, last_line_num, i; - const char *str, *p; - Stab_Sym *sym; - -next: - func_name[0] = '\0'; - func_addr = 0; - incl_index = 0; - last_pc = (addr_t)-1; - last_line_num = 1; - last_incl_index = 0; - - for (sym = rc->stab_sym + 1; sym < rc->stab_sym_end; ++sym) { - str = rc->stab_str + sym->n_strx; - pc = sym->n_value; - - switch(sym->n_type) { - case N_SLINE: - if (func_addr) - goto rel_pc; - case N_SO: - case N_SOL: - goto abs_pc; - case N_FUN: - if (sym->n_strx == 0) /* end of function */ - goto rel_pc; - abs_pc: -#if PTR_SIZE == 8 - /* Stab_Sym.n_value is only 32bits */ - pc += rc->prog_base; -#endif - goto check_pc; - rel_pc: - pc += func_addr; - check_pc: - if (pc >= wanted_pc && wanted_pc >= last_pc) - goto found; - break; - } - - switch(sym->n_type) { - /* function start or end */ - case N_FUN: - if (sym->n_strx == 0) - goto reset_func; - p = strchr(str, ':'); - if (0 == p || (len = p - str + 1, len > sizeof func_name)) - len = sizeof func_name; - pstrcpy(func_name, len, str); - func_addr = pc; - break; - /* line number info */ - case N_SLINE: - last_pc = pc; - last_line_num = sym->n_desc; - last_incl_index = incl_index; - break; - /* include files */ - case N_BINCL: - if (incl_index < INCLUDE_STACK_SIZE) - incl_files[incl_index++] = str; - break; - case N_EINCL: - if (incl_index > 1) - incl_index--; - break; - /* start/end of translation unit */ - case N_SO: - incl_index = 0; - if (sym->n_strx) { - /* do not add path */ - len = strlen(str); - if (len > 0 && str[len - 1] != '/') - incl_files[incl_index++] = str; - } - reset_func: - func_name[0] = '\0'; - func_addr = 0; - last_pc = (addr_t)-1; - break; - /* alternative file name (from #line or #include directives) */ - case N_SOL: - if (incl_index) - incl_files[incl_index-1] = str; - break; - } - } - - func_name[0] = '\0'; - func_addr = 0; - last_incl_index = 0; - /* we try symtab symbols (no line number info) */ - p = rt_elfsym(rc, wanted_pc, &func_addr); - if (p) { - pstrcpy(func_name, sizeof func_name, p); - goto found; - } - if ((rc = rc->next)) - goto next; -found: - i = last_incl_index; - 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 0 - if (--i >= 0) { - rt_printf(" (included from "); - for (;;) { - rt_printf("%s", incl_files[i]); - if (--i < 0) - break; - rt_printf(", "); - } - rt_printf(")"); - } -#endif - return func_addr; -} - -/* ------------------------------------------------------------- */ -/* rt_printline - dwarf version */ - -#define MAX_128 ((8 * sizeof (long long) + 6) / 7) - -#define DIR_TABLE_SIZE (64) -#define FILE_TABLE_SIZE (512) - -#define dwarf_read_1(ln,end) \ - ((ln) < (end) ? *(ln)++ : 0) -#define dwarf_read_2(ln,end) \ - ((ln) + 2 < (end) ? (ln) += 2, read16le((ln) - 2) : 0) -#define dwarf_read_4(ln,end) \ - ((ln) + 4 < (end) ? (ln) += 4, read32le((ln) - 4) : 0) -#define dwarf_read_8(ln,end) \ - ((ln) + 8 < (end) ? (ln) += 8, read64le((ln) - 8) : 0) -#define dwarf_ignore_type(ln, end) /* timestamp/size/md5/... */ \ - switch (entry_format[j].form) { \ - case DW_FORM_data1: (ln) += 1; break; \ - case DW_FORM_data2: (ln) += 2; break; \ - case DW_FORM_data4: (ln) += 3; break; \ - case DW_FORM_data8: (ln) += 8; break; \ - case DW_FORM_data16: (ln) += 16; break; \ - case DW_FORM_udata: dwarf_read_uleb128(&(ln), (end)); break; \ - default: goto next_line; \ - } - -static unsigned long long -dwarf_read_uleb128(unsigned char **ln, unsigned char *end) -{ - unsigned char *cp = *ln; - unsigned long long retval = 0; - int i; - - for (i = 0; i < MAX_128; i++) { - unsigned long long byte = dwarf_read_1(cp, end); - - retval |= (byte & 0x7f) << (i * 7); - if ((byte & 0x80) == 0) - break; - } - *ln = cp; - return retval; -} - -static long long -dwarf_read_sleb128(unsigned char **ln, unsigned char *end) -{ - unsigned char *cp = *ln; - long long retval = 0; - int i; - - for (i = 0; i < MAX_128; i++) { - unsigned long long byte = dwarf_read_1(cp, end); - - retval |= (byte & 0x7f) << (i * 7); - if ((byte & 0x80) == 0) { - if ((byte & 0x40) && (i + 1) * 7 < 64) - retval |= -1LL << ((i + 1) * 7); - break; - } - } - *ln = cp; - return retval; -} - -static addr_t rt_printline_dwarf (rt_context *rc, addr_t wanted_pc, - const char *msg, const char *skip) -{ - unsigned char *ln; - unsigned char *cp; - unsigned char *end; - unsigned char *opcode_length; - unsigned long long size; - unsigned int length; - unsigned char version; - unsigned int min_insn_length; - unsigned int max_ops_per_insn; - int line_base; - unsigned int line_range; - unsigned int opcode_base; - unsigned int opindex; - unsigned int col; - unsigned int i; - unsigned int j; - unsigned int len; - unsigned long long value; - struct { - unsigned int type; - unsigned int form; - } entry_format[256]; - unsigned int dir_size; -#if 0 - char *dirs[DIR_TABLE_SIZE]; -#endif - unsigned int filename_size; - struct dwarf_filename_struct { - unsigned int dir_entry; - char *name; - } filename_table[FILE_TABLE_SIZE]; - addr_t last_pc; - addr_t pc; - addr_t func_addr; - int line; - char *filename; - char *function; - -next: - ln = rc->dwarf_line; - while (ln < rc->dwarf_line_end) { - dir_size = 0; - filename_size = 0; - last_pc = 0; - pc = 0; - func_addr = 0; - line = 1; - filename = NULL; - function = NULL; - length = 4; - size = dwarf_read_4(ln, rc->dwarf_line_end); - if (size == 0xffffffffu) // dwarf 64 - length = 8, size = dwarf_read_8(ln, rc->dwarf_line_end); - end = ln + size; - if (end < ln || end > rc->dwarf_line_end) - break; - version = dwarf_read_2(ln, end); - if (version >= 5) - ln += length + 2; // address size, segment selector, prologue Length - else - ln += length; // prologue Length - min_insn_length = dwarf_read_1(ln, end); - if (version >= 4) - max_ops_per_insn = dwarf_read_1(ln, end); - else - max_ops_per_insn = 1; - ln++; // Initial value of 'is_stmt' - line_base = dwarf_read_1(ln, end); - line_base |= line_base >= 0x80 ? ~0xff : 0; - line_range = dwarf_read_1(ln, end); - opcode_base = dwarf_read_1(ln, end); - opcode_length = ln; - ln += opcode_base - 1; - opindex = 0; - if (version >= 5) { - col = dwarf_read_1(ln, end); - for (i = 0; i < col; i++) { - entry_format[i].type = dwarf_read_uleb128(&ln, end); - entry_format[i].form = dwarf_read_uleb128(&ln, end); - } - dir_size = dwarf_read_uleb128(&ln, end); - for (i = 0; i < dir_size; i++) { - for (j = 0; j < col; j++) { - if (entry_format[j].type == DW_LNCT_path) { - if (entry_format[j].form != DW_FORM_line_strp) - goto next_line; -#if 0 - value = length == 4 ? dwarf_read_4(ln, end) - : dwarf_read_8(ln, end); - if (i < DIR_TABLE_SIZE) - dirs[i] = (char *)rc->dwarf_line_str + value; -#else - length == 4 ? dwarf_read_4(ln, end) - : dwarf_read_8(ln, end); -#endif - } - else - dwarf_ignore_type(ln, end); - } - } - col = dwarf_read_1(ln, end); - for (i = 0; i < col; i++) { - entry_format[i].type = dwarf_read_uleb128(&ln, end); - entry_format[i].form = dwarf_read_uleb128(&ln, end); - } - filename_size = dwarf_read_uleb128(&ln, end); - for (i = 0; i < filename_size; i++) - for (j = 0; j < col; j++) { - if (entry_format[j].type == DW_LNCT_path) { - if (entry_format[j].form != DW_FORM_line_strp) - goto next_line; - value = length == 4 ? dwarf_read_4(ln, end) - : dwarf_read_8(ln, end); - if (i < FILE_TABLE_SIZE) - filename_table[i].name = - (char *)rc->dwarf_line_str + value; - } - else if (entry_format[j].type == DW_LNCT_directory_index) { - switch (entry_format[j].form) { - case DW_FORM_data1: value = dwarf_read_1(ln, end); break; - case DW_FORM_data2: value = dwarf_read_2(ln, end); break; - case DW_FORM_data4: value = dwarf_read_4(ln, end); break; - case DW_FORM_udata: value = dwarf_read_uleb128(&ln, end); break; - default: goto next_line; - } - if (i < FILE_TABLE_SIZE) - filename_table[i].dir_entry = value; - } - else - dwarf_ignore_type(ln, end); - } - } - else { - while ((dwarf_read_1(ln, end))) { -#if 0 - if (++dir_size < DIR_TABLE_SIZE) - dirs[dir_size - 1] = (char *)ln - 1; -#endif - while (dwarf_read_1(ln, end)) {} - } - while ((dwarf_read_1(ln, end))) { - if (++filename_size < FILE_TABLE_SIZE) { - filename_table[filename_size - 1].name = (char *)ln - 1; - while (dwarf_read_1(ln, end)) {} - filename_table[filename_size - 1].dir_entry = - dwarf_read_uleb128(&ln, end); - } - else { - while (dwarf_read_1(ln, end)) {} - dwarf_read_uleb128(&ln, end); - } - dwarf_read_uleb128(&ln, end); // time - dwarf_read_uleb128(&ln, end); // size - } - } - if (filename_size >= 1) - filename = filename_table[0].name; - while (ln < end) { - last_pc = pc; - i = dwarf_read_1(ln, end); - if (i >= opcode_base) { - if (max_ops_per_insn == 1) - pc += ((i - opcode_base) / line_range) * min_insn_length; - else { - pc += (opindex + (i - opcode_base) / line_range) / - max_ops_per_insn * min_insn_length; - opindex = (opindex + (i - opcode_base) / line_range) % - max_ops_per_insn; - } - i = (int)((i - opcode_base) % line_range) + line_base; -check_pc: - if (pc >= wanted_pc && wanted_pc >= last_pc) - goto found; - line += i; - } - else { - switch (i) { - case 0: - len = dwarf_read_uleb128(&ln, end); - cp = ln; - ln += len; - if (len == 0) - goto next_line; - switch (dwarf_read_1(cp, end)) { - case DW_LNE_end_sequence: - break; - case DW_LNE_set_address: -#if PTR_SIZE == 4 - pc = dwarf_read_4(cp, end); -#else - pc = dwarf_read_8(cp, end); -#endif -#if defined TCC_TARGET_MACHO - if (rc->prog_base != (addr_t) -1) - pc += rc->prog_base; -#endif - opindex = 0; - break; - case DW_LNE_define_file: /* deprecated */ - if (++filename_size < FILE_TABLE_SIZE) { - filename_table[filename_size - 1].name = (char *)ln - 1; - while (dwarf_read_1(ln, end)) {} - filename_table[filename_size - 1].dir_entry = - dwarf_read_uleb128(&ln, end); - } - else { - while (dwarf_read_1(ln, end)) {} - dwarf_read_uleb128(&ln, end); - } - dwarf_read_uleb128(&ln, end); // time - dwarf_read_uleb128(&ln, end); // size - break; - case DW_LNE_hi_user - 1: - function = (char *)cp; - func_addr = pc; - break; - default: - break; - } - break; - case DW_LNS_advance_pc: - if (max_ops_per_insn == 1) - pc += dwarf_read_uleb128(&ln, end) * min_insn_length; - else { - unsigned long long off = dwarf_read_uleb128(&ln, end); - - pc += (opindex + off) / max_ops_per_insn * - min_insn_length; - opindex = (opindex + off) % max_ops_per_insn; - } - i = 0; - goto check_pc; - case DW_LNS_advance_line: - line += dwarf_read_sleb128(&ln, end); - break; - case DW_LNS_set_file: - i = dwarf_read_uleb128(&ln, end); - i -= i > 0 && version < 5; - if (i < FILE_TABLE_SIZE && i < filename_size) - filename = filename_table[i].name; - break; - case DW_LNS_const_add_pc: - if (max_ops_per_insn == 1) - pc += ((255 - opcode_base) / line_range) * min_insn_length; - else { - unsigned int off = (255 - opcode_base) / line_range; - - pc += ((opindex + off) / max_ops_per_insn) * - min_insn_length; - opindex = (opindex + off) % max_ops_per_insn; - } - i = 0; - goto check_pc; - case DW_LNS_fixed_advance_pc: - i = dwarf_read_2(ln, end); - pc += i; - opindex = 0; - i = 0; - goto check_pc; - default: - for (j = 0; j < opcode_length[i - 1]; j++) - dwarf_read_uleb128 (&ln, end); - break; - } - } - } -next_line: - ln = end; - } - - filename = NULL; - func_addr = 0; - /* we try symtab symbols (no line number info) */ - function = rt_elfsym(rc, wanted_pc, &func_addr); - if (function) - goto found; - if ((rc = rc->next)) - goto next; -found: - 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 : "???"); - return (addr_t)func_addr; -} -/* ------------------------------------------------------------- */ - -static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level); - -static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap) -{ - rt_context *rc = &g_rtctxt; - addr_t pc = 0; - char skip[100]; - int i, level, ret, n; - const char *a, *b, *msg; - - if (fp) { - /* we're called from tcc_backtrace. */ - rc->fp = (addr_t)fp; - rc->ip = (addr_t)ip; - msg = ""; - } else { - /* we're called from signal/exception handler */ - msg = "RUNTIME ERROR: "; - } - - skip[0] = 0; - /* If fmt is like "^file.c^..." then skip calls from 'file.c' */ - if (fmt[0] == '^' && (b = strchr(a = fmt + 1, fmt[0]))) { - memcpy(skip, a, b - a), skip[b - a] = 0; - fmt = b + 1; - } - - n = rc->num_callers ? rc->num_callers : 6; - for (i = level = 0; level < n; i++) { - ret = rt_get_caller_pc(&pc, rc, i); - a = "%s"; - if (ret != -1) { - if (rc->dwarf) - pc = rt_printline_dwarf(rc, pc, level ? "by" : "at", skip); - else - pc = rt_printline(rc, pc, level ? "by" : "at", skip); - if (pc == (addr_t)-1) - continue; - a = ": %s"; - } - if (level == 0) { - rt_printf(a, msg); - rt_vprintf(fmt, ap); - } else if (ret == -1) - break; - rt_printf("\n"); - if (ret == -1 || (pc == (addr_t)rc->top_func && pc)) - break; - ++level; - } - - rc->ip = rc->fp = 0; - return 0; -} - -/* emit a run time error at position 'pc' */ -static int rt_error(const char *fmt, ...) -{ - va_list ap; - int ret; - va_start(ap, fmt); - ret = _rt_error(0, 0, fmt, ap); - va_end(ap); - return ret; -} - -static void rt_exit(int code) -{ - rt_context *rc = &g_rtctxt; - if (rc->do_jmp) - longjmp(rc->jmp_buf, code ? code : 256); - exit(code); -} - -/* ------------------------------------------------------------- */ - -#ifndef _WIN32 -# include <signal.h> -# ifndef __OpenBSD__ -# include <sys/ucontext.h> -# endif -#else -# define ucontext_t CONTEXT -#endif - -/* translate from ucontext_t* to internal rt_context * */ -static void rt_getcontext(ucontext_t *uc, rt_context *rc) -{ -#if defined _WIN64 - rc->ip = uc->Rip; - rc->fp = uc->Rbp; - rc->sp = uc->Rsp; -#elif defined _WIN32 - rc->ip = uc->Eip; - rc->fp = uc->Ebp; - rc->sp = uc->Esp; -#elif defined __i386__ -# if defined(__APPLE__) - rc->ip = uc->uc_mcontext->__ss.__eip; - rc->fp = uc->uc_mcontext->__ss.__ebp; -# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) - rc->ip = uc->uc_mcontext.mc_eip; - rc->fp = uc->uc_mcontext.mc_ebp; -# elif defined(__dietlibc__) - rc->ip = uc->uc_mcontext.eip; - rc->fp = uc->uc_mcontext.ebp; -# elif defined(__NetBSD__) - rc->ip = uc->uc_mcontext.__gregs[_REG_EIP]; - rc->fp = uc->uc_mcontext.__gregs[_REG_EBP]; -# elif defined(__OpenBSD__) - rc->ip = uc->sc_eip; - rc->fp = uc->sc_ebp; -# elif !defined REG_EIP && defined EIP /* fix for glibc 2.1 */ - rc->ip = uc->uc_mcontext.gregs[EIP]; - rc->fp = uc->uc_mcontext.gregs[EBP]; -# else - rc->ip = uc->uc_mcontext.gregs[REG_EIP]; - rc->fp = uc->uc_mcontext.gregs[REG_EBP]; -# endif -#elif defined(__x86_64__) -# if defined(__APPLE__) - rc->ip = uc->uc_mcontext->__ss.__rip; - rc->fp = uc->uc_mcontext->__ss.__rbp; -# elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) - rc->ip = uc->uc_mcontext.mc_rip; - rc->fp = uc->uc_mcontext.mc_rbp; -# elif defined(__NetBSD__) - rc->ip = uc->uc_mcontext.__gregs[_REG_RIP]; - rc->fp = uc->uc_mcontext.__gregs[_REG_RBP]; -# elif defined(__OpenBSD__) - rc->ip = uc->sc_rip; - rc->fp = uc->sc_rbp; -# else - rc->ip = uc->uc_mcontext.gregs[REG_RIP]; - rc->fp = uc->uc_mcontext.gregs[REG_RBP]; -# endif -#elif defined(__arm__) && defined(__NetBSD__) - rc->ip = uc->uc_mcontext.__gregs[_REG_PC]; - rc->fp = uc->uc_mcontext.__gregs[_REG_FP]; -#elif defined(__arm__) && defined(__OpenBSD__) - rc->ip = uc->sc_pc; - rc->fp = uc->sc_r11; -#elif defined(__arm__) && defined(__FreeBSD__) - rc->ip = uc->uc_mcontext.__gregs[_REG_PC]; - rc->fp = uc->uc_mcontext.__gregs[_REG_FP]; -#elif defined(__arm__) - rc->ip = uc->uc_mcontext.arm_pc; - rc->fp = uc->uc_mcontext.arm_fp; -#elif defined(__aarch64__) && defined(__APPLE__) - // see: - // /Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include/mach/arm/_structs.h - rc->ip = uc->uc_mcontext->__ss.__pc; - rc->fp = uc->uc_mcontext->__ss.__fp; -#elif defined(__aarch64__) && defined(__FreeBSD__) - rc->ip = uc->uc_mcontext.mc_gpregs.gp_elr; /* aka REG_PC */ - rc->fp = uc->uc_mcontext.mc_gpregs.gp_x[29]; -#elif defined(__aarch64__) && defined(__NetBSD__) - rc->ip = uc->uc_mcontext.__gregs[_REG_PC]; - rc->fp = uc->uc_mcontext.__gregs[_REG_FP]; -#elif defined(__aarch64__) && defined(__OpenBSD__) - rc->ip = uc->sc_elr; - rc->fp = uc->sc_x[29]; -#elif defined(__aarch64__) - rc->ip = uc->uc_mcontext.pc; - rc->fp = uc->uc_mcontext.regs[29]; -#elif defined(__riscv) && defined(__OpenBSD__) - rc->ip = uc->sc_sepc; - rc->fp = uc->sc_s[0]; -#elif defined(__riscv) - rc->ip = uc->uc_mcontext.__gregs[REG_PC]; - rc->fp = uc->uc_mcontext.__gregs[REG_S0]; -#endif -} - -/* ------------------------------------------------------------- */ -#ifndef _WIN32 -/* signal handler for fatal errors */ -static void sig_error(int signum, siginfo_t *siginf, void *puc) -{ - rt_context *rc = &g_rtctxt; - rt_getcontext(puc, rc); - - switch(signum) { - case SIGFPE: - switch(siginf->si_code) { - case FPE_INTDIV: - case FPE_FLTDIV: - rt_error("division by zero"); - break; - default: - rt_error("floating point exception"); - break; - } - break; - case SIGBUS: - case SIGSEGV: - rt_error("invalid memory access"); - break; - case SIGILL: - rt_error("illegal instruction"); - break; - case SIGABRT: - rt_error("abort() called"); - break; - default: - rt_error("caught signal %d", signum); - break; - } - rt_exit(255); -} - -#ifndef SA_SIGINFO -# define SA_SIGINFO 0x00000004u -#endif - -/* Generate a stack backtrace when a CPU exception occurs. */ -static void set_exception_handler(void) -{ - struct sigaction sigact; - /* install TCC signal handlers to print debug info on fatal - runtime errors */ - sigemptyset (&sigact.sa_mask); - sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; -#if 0//def SIGSTKSZ // this causes signals not to work at all on some (older) linuxes - sigact.sa_flags |= SA_ONSTACK; -#endif - sigact.sa_sigaction = sig_error; - sigemptyset(&sigact.sa_mask); - sigaction(SIGFPE, &sigact, NULL); - sigaction(SIGILL, &sigact, NULL); - sigaction(SIGSEGV, &sigact, NULL); - sigaction(SIGBUS, &sigact, NULL); - sigaction(SIGABRT, &sigact, NULL); -#if 0//def SIGSTKSZ - /* This allows stack overflow to be reported instead of a SEGV */ - { - stack_t ss; - static unsigned char stack[SIGSTKSZ] __attribute__((aligned(16))); - - ss.ss_sp = stack; - ss.ss_size = SIGSTKSZ; - ss.ss_flags = 0; - sigaltstack(&ss, NULL); - } -#endif -} - -#else /* WIN32 */ - -/* signal handler for fatal errors */ -static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info) -{ - rt_context *rc = &g_rtctxt; - unsigned code; - rt_getcontext(ex_info->ContextRecord, rc); - - switch (code = ex_info->ExceptionRecord->ExceptionCode) { - case EXCEPTION_ACCESS_VIOLATION: - rt_error("invalid memory access"); - break; - case EXCEPTION_STACK_OVERFLOW: - rt_error("stack overflow"); - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - rt_error("division by zero"); - break; - case EXCEPTION_BREAKPOINT: - case EXCEPTION_SINGLE_STEP: - rc->ip = *(addr_t*)rc->sp; - rt_error("breakpoint/single-step exception:"); - return EXCEPTION_CONTINUE_SEARCH; - default: - rt_error("caught exception %08x", code); - break; - } - if (rc->do_jmp) - rt_exit(255); - return EXCEPTION_EXECUTE_HANDLER; -} - -/* Generate a stack backtrace when a CPU exception occurs. */ -static void set_exception_handler(void) -{ - SetUnhandledExceptionFilter(cpu_exception_handler); -} - -#endif - -/* ------------------------------------------------------------- */ -/* return the PC at frame level 'level'. Return negative if not found */ -#if defined(__i386__) || defined(__x86_64__) -static int rt_get_caller_pc(addr_t *paddr, rt_context *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_context *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) - fp = ((addr_t *)fp)[0]; - *paddr = ((addr_t *)fp)[2]; - } - return 0; -#endif -} - -#elif defined(__aarch64__) -static int rt_get_caller_pc(addr_t *paddr, rt_context *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]; - } - return 0; -} - -#elif defined(__riscv) -static int rt_get_caller_pc(addr_t *paddr, rt_context *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]; - } - return 0; -} - -#else -#warning add arch specific rt_get_caller_pc() -static int rt_get_caller_pc(addr_t *paddr, rt_context *rc, int level) -{ - return -1; -} - -#endif -#endif /* CONFIG_TCC_BACKTRACE */ -/* ------------------------------------------------------------- */ -#ifdef CONFIG_TCC_STATIC - -/* dummy function for profiling */ -ST_FUNC void *dlopen(const char *filename, int flag) -{ - return NULL; -} - -ST_FUNC void dlclose(void *p) -{ -} - -ST_FUNC const char *dlerror(void) -{ - return "error"; -} - -typedef struct TCCSyms { - char *str; - void *ptr; -} TCCSyms; - - -/* add the symbol you want here if no dynamic linking is done */ -static TCCSyms tcc_syms[] = { -#if !defined(CONFIG_TCCBOOT) -#define TCCSYM(a) { #a, &a, }, - TCCSYM(printf) - TCCSYM(fprintf) - TCCSYM(fopen) - TCCSYM(fclose) -#undef TCCSYM -#endif - { NULL, NULL }, -}; - -ST_FUNC void *dlsym(void *handle, const char *symbol) -{ - TCCSyms *p; - p = tcc_syms; - while (p->str != NULL) { - if (!strcmp(p->str, symbol)) - return p->ptr; - p++; - } - return NULL; -} - -#endif /* CONFIG_TCC_STATIC */ -#endif /* TCC_IS_NATIVE */ -/* ------------------------------------------------------------- */ |
