aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrischka <grischka>2009-12-19 22:10:26 +0100
committergrischka <grischka>2009-12-19 22:16:22 +0100
commit3db219477a6365d0f1d5214a345b3393a38e81d1 (patch)
treecab579d5f0489fe3a82cc2126f57ad043f754a00
parent41e112360f77c53a962b7b503ecaf9e2e39643e5 (diff)
downloadtinycc-3db219477a6365d0f1d5214a345b3393a38e81d1.tar.gz
tinycc-3db219477a6365d0f1d5214a345b3393a38e81d1.tar.bz2
tccrun: new file
factor out -run support
-rw-r--r--libtcc.c506
-rw-r--r--tccrun.c518
2 files changed, 527 insertions, 497 deletions
diff --git a/libtcc.c b/libtcc.c
index 5b11396..2afe432 100644
--- a/libtcc.c
+++ b/libtcc.c
@@ -352,6 +352,8 @@ static void asm_global_instr(void)
#include "tccpe.c"
#endif
+#include "tccrun.c"
+
/********************************************************/
#ifdef _WIN32
char *normalize_slashes(char *path)
@@ -390,72 +392,6 @@ BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
#endif
/********************************************************/
-#ifdef CONFIG_TCC_STATIC
-
-#define RTLD_LAZY 0x001
-#define RTLD_NOW 0x002
-#define RTLD_GLOBAL 0x100
-#define RTLD_DEFAULT NULL
-
-/* dummy function for profiling */
-void *dlopen(const char *filename, int flag)
-{
- return NULL;
-}
-
-void dlclose(void *p)
-{
-}
-
-const char *dlerror(void)
-{
- return "error";
-}
-
-typedef struct TCCSyms {
- char *str;
- void *ptr;
-} TCCSyms;
-
-#define TCCSYM(a) { #a, &a, },
-
-/* add the symbol you want here if no dynamic linking is done */
-static TCCSyms tcc_syms[] = {
-#if !defined(CONFIG_TCCBOOT)
- TCCSYM(printf)
- TCCSYM(fprintf)
- TCCSYM(fopen)
- TCCSYM(fclose)
-#endif
- { NULL, NULL },
-};
-
-void *resolve_sym(TCCState *s1, const char *symbol)
-{
- TCCSyms *p;
- p = tcc_syms;
- while (p->str != NULL) {
- if (!strcmp(p->str, symbol))
- return p->ptr;
- p++;
- }
- return NULL;
-}
-
-#elif defined(_WIN32)
-#define dlclose FreeLibrary
-
-#else
-#include <dlfcn.h>
-
-void *resolve_sym(TCCState *s1, const char *sym)
-{
- return dlsym(RTLD_DEFAULT, sym);
-}
-
-#endif
-
-/********************************************************/
/* we use our own 'finite' function to avoid potential problems with
non standard math libs */
@@ -512,20 +448,6 @@ char *tcc_fileextension (const char *name)
return e ? e : strchr(b, 0);
}
-void set_pages_executable(void *ptr, unsigned long length)
-{
-#ifdef _WIN32
- unsigned long old_protect;
- VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
-#else
- unsigned long start, end;
- start = (unsigned long)ptr & ~(PAGESIZE - 1);
- end = (unsigned long)ptr + length;
- end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
- mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
-#endif
-}
-
/* memory management */
#ifdef MEM_DEBUG
int mem_cur_size;
@@ -587,6 +509,13 @@ char *tcc_strdup(const char *str)
return ptr;
}
+void tcc_memstats(void)
+{
+#ifdef MEM_DEBUG
+ printf("memory in use: %d\n", mem_cur_size);
+#endif
+}
+
#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
@@ -1409,423 +1338,6 @@ void tcc_undefine_symbol(TCCState *s1, const char *sym)
define_undef(s);
}
-
-#ifdef CONFIG_TCC_BACKTRACE
-/* print the position in the source file of PC value 'pc' by reading
- the stabs debug information */
-static unsigned long rt_printline(unsigned long wanted_pc)
-{
- Stab_Sym *sym, *sym_end;
- char func_name[128], last_func_name[128];
- unsigned long func_addr, last_pc, pc;
- const char *incl_files[INCLUDE_STACK_SIZE];
- int incl_index, len, last_line_num, i;
- const char *str, *p;
-
- fprintf(stderr, "0x%08lx:", wanted_pc);
-
- func_name[0] = '\0';
- func_addr = 0;
- incl_index = 0;
- last_func_name[0] = '\0';
- last_pc = 0xffffffff;
- last_line_num = 1;
- sym = (Stab_Sym *)stab_section->data + 1;
- sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
- while (sym < sym_end) {
- switch(sym->n_type) {
- /* function start or end */
- case N_FUN:
- if (sym->n_strx == 0) {
- /* we test if between last line and end of function */
- pc = sym->n_value + func_addr;
- if (wanted_pc >= last_pc && wanted_pc < pc)
- goto found;
- func_name[0] = '\0';
- func_addr = 0;
- } else {
- str = stabstr_section->data + sym->n_strx;
- p = strchr(str, ':');
- if (!p) {
- pstrcpy(func_name, sizeof(func_name), str);
- } else {
- len = p - str;
- if (len > sizeof(func_name) - 1)
- len = sizeof(func_name) - 1;
- memcpy(func_name, str, len);
- func_name[len] = '\0';
- }
- func_addr = sym->n_value;
- }
- break;
- /* line number info */
- case N_SLINE:
- pc = sym->n_value + func_addr;
- if (wanted_pc >= last_pc && wanted_pc < pc)
- goto found;
- last_pc = pc;
- last_line_num = sym->n_desc;
- /* XXX: slow! */
- strcpy(last_func_name, func_name);
- break;
- /* include files */
- case N_BINCL:
- str = stabstr_section->data + sym->n_strx;
- add_incl:
- if (incl_index < INCLUDE_STACK_SIZE) {
- incl_files[incl_index++] = str;
- }
- break;
- case N_EINCL:
- if (incl_index > 1)
- incl_index--;
- break;
- case N_SO:
- if (sym->n_strx == 0) {
- incl_index = 0; /* end of translation unit */
- } else {
- str = stabstr_section->data + sym->n_strx;
- /* do not add path */
- len = strlen(str);
- if (len > 0 && str[len - 1] != '/')
- goto add_incl;
- }
- break;
- }
- sym++;
- }
-
- /* second pass: we try symtab symbols (no line number info) */
- incl_index = 0;
- {
- ElfW(Sym) *sym, *sym_end;
- int type;
-
- sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
- for(sym = (ElfW(Sym) *)symtab_section->data + 1;
- sym < sym_end;
- sym++) {
- type = ELFW(ST_TYPE)(sym->st_info);
- if (type == STT_FUNC) {
- if (wanted_pc >= sym->st_value &&
- wanted_pc < sym->st_value + sym->st_size) {
- pstrcpy(last_func_name, sizeof(last_func_name),
- strtab_section->data + sym->st_name);
- func_addr = sym->st_value;
- goto found;
- }
- }
- }
- }
- /* did not find any info: */
- fprintf(stderr, " ???\n");
- return 0;
- found:
- if (last_func_name[0] != '\0') {
- fprintf(stderr, " %s()", last_func_name);
- }
- if (incl_index > 0) {
- fprintf(stderr, " (%s:%d",
- incl_files[incl_index - 1], last_line_num);
- for(i = incl_index - 2; i >= 0; i--)
- fprintf(stderr, ", included from %s", incl_files[i]);
- fprintf(stderr, ")");
- }
- fprintf(stderr, "\n");
- return func_addr;
-}
-
-#ifdef __i386__
-/* fix for glibc 2.1 */
-#ifndef REG_EIP
-#define REG_EIP EIP
-#define REG_EBP EBP
-#endif
-
-/* return the PC at frame level 'level'. Return non zero if not found */
-static int rt_get_caller_pc(unsigned long *paddr,
- ucontext_t *uc, int level)
-{
- unsigned long fp;
- int i;
-
- if (level == 0) {
-#if defined(__FreeBSD__)
- *paddr = uc->uc_mcontext.mc_eip;
-#elif defined(__dietlibc__)
- *paddr = uc->uc_mcontext.eip;
-#else
- *paddr = uc->uc_mcontext.gregs[REG_EIP];
-#endif
- return 0;
- } else {
-#if defined(__FreeBSD__)
- fp = uc->uc_mcontext.mc_ebp;
-#elif defined(__dietlibc__)
- fp = uc->uc_mcontext.ebp;
-#else
- fp = uc->uc_mcontext.gregs[REG_EBP];
-#endif
- for(i=1;i<level;i++) {
- /* XXX: check address validity with program info */
- if (fp <= 0x1000 || fp >= 0xc0000000)
- return -1;
- fp = ((unsigned long *)fp)[0];
- }
- *paddr = ((unsigned long *)fp)[1];
- return 0;
- }
-}
-#elif defined(__x86_64__)
-/* return the PC at frame level 'level'. Return non zero if not found */
-static int rt_get_caller_pc(unsigned long *paddr,
- ucontext_t *uc, int level)
-{
- unsigned long fp;
- int i;
-
- if (level == 0) {
- /* XXX: only support linux */
-#if defined(__FreeBSD__)
- *paddr = uc->uc_mcontext.mc_rip;
-#else
- *paddr = uc->uc_mcontext.gregs[REG_RIP];
-#endif
- return 0;
- } else {
-#if defined(__FreeBSD__)
- fp = uc->uc_mcontext.mc_rbp;
-#else
- fp = uc->uc_mcontext.gregs[REG_RBP];
-#endif
- for(i=1;i<level;i++) {
- /* XXX: check address validity with program info */
- if (fp <= 0x1000)
- return -1;
- fp = ((unsigned long *)fp)[0];
- }
- *paddr = ((unsigned long *)fp)[1];
- return 0;
- }
-}
-#else
-#warning add arch specific rt_get_caller_pc()
-static int rt_get_caller_pc(unsigned long *paddr,
- ucontext_t *uc, int level)
-{
- return -1;
-}
-#endif
-
-/* emit a run time error at position 'pc' */
-void rt_error(ucontext_t *uc, const char *fmt, ...)
-{
- va_list ap;
- unsigned long pc;
- int i;
-
- va_start(ap, fmt);
- fprintf(stderr, "Runtime error: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- for(i=0;i<num_callers;i++) {
- if (rt_get_caller_pc(&pc, uc, i) < 0)
- break;
- if (i == 0)
- fprintf(stderr, "at ");
- else
- fprintf(stderr, "by ");
- pc = rt_printline(pc);
- if (pc == rt_prog_main && pc)
- break;
- }
- exit(255);
- va_end(ap);
-}
-
-/* signal handler for fatal errors */
-static void sig_error(int signum, siginfo_t *siginf, void *puc)
-{
- ucontext_t *uc = puc;
-
- switch(signum) {
- case SIGFPE:
- switch(siginf->si_code) {
- case FPE_INTDIV:
- case FPE_FLTDIV:
- rt_error(uc, "division by zero");
- break;
- default:
- rt_error(uc, "floating point exception");
- break;
- }
- break;
- case SIGBUS:
- case SIGSEGV:
- if (rt_bound_error_msg && *rt_bound_error_msg)
- rt_error(uc, *rt_bound_error_msg);
- else
- rt_error(uc, "dereferencing invalid pointer");
- break;
- case SIGILL:
- rt_error(uc, "illegal instruction");
- break;
- case SIGABRT:
- rt_error(uc, "abort() called");
- break;
- default:
- rt_error(uc, "caught signal %d", signum);
- break;
- }
- exit(255);
-}
-
-#endif
-
-/* copy code into memory passed in by the caller and do all relocations
- (needed before using tcc_get_symbol()).
- returns -1 on error and required size if ptr is NULL */
-int tcc_relocate(TCCState *s1, void *ptr)
-{
- Section *s;
- unsigned long offset, length;
- uplong mem;
- int i;
-
- if (0 == s1->runtime_added) {
- s1->runtime_added = 1;
- s1->nb_errors = 0;
-#ifdef TCC_TARGET_PE
- pe_output_file(s1, NULL);
-#else
- tcc_add_runtime(s1);
- relocate_common_syms();
- tcc_add_linker_symbols(s1);
- build_got_entries(s1);
-#endif
- if (s1->nb_errors)
- return -1;
- }
-
- offset = 0, mem = (uplong)ptr;
- for(i = 1; i < s1->nb_sections; i++) {
- s = s1->sections[i];
- if (0 == (s->sh_flags & SHF_ALLOC))
- continue;
- length = s->data_offset;
- s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0;
- offset = (offset + length + 15) & ~15;
- }
-
- /* relocate symbols */
- relocate_syms(s1, 1);
- if (s1->nb_errors)
- return -1;
-
-#ifndef TCC_TARGET_PE
-#ifdef TCC_TARGET_X86_64
- s1->runtime_plt_and_got_offset = 0;
- s1->runtime_plt_and_got = (char *)(mem + offset);
- /* double the size of the buffer for got and plt entries
- XXX: calculate exact size for them? */
- offset *= 2;
-#endif
-#endif
-
- if (0 == mem)
- return offset + 15;
-
- /* relocate each section */
- for(i = 1; i < s1->nb_sections; i++) {
- s = s1->sections[i];
- if (s->reloc)
- relocate_section(s1, s);
- }
-
- for(i = 1; i < s1->nb_sections; i++) {
- s = s1->sections[i];
- if (0 == (s->sh_flags & SHF_ALLOC))
- continue;
- length = s->data_offset;
- // printf("%-12s %08x %04x\n", s->name, s->sh_addr, length);
- ptr = (void*)(uplong)s->sh_addr;
- if (NULL == s->data || s->sh_type == SHT_NOBITS)
- memset(ptr, 0, length);
- else
- memcpy(ptr, s->data, length);
- /* mark executable sections as executable in memory */
- if (s->sh_flags & SHF_EXECINSTR)
- set_pages_executable(ptr, length);
- }
-#ifndef TCC_TARGET_PE
-#ifdef TCC_TARGET_X86_64
- set_pages_executable(s1->runtime_plt_and_got,
- s1->runtime_plt_and_got_offset);
-#endif
-#endif
- return 0;
-}
-
-/* launch the compiled program with the given arguments */
-int tcc_run(TCCState *s1, int argc, char **argv)
-{
- int (*prog_main)(int, char **);
- void *ptr;
- int ret;
-
- ret = tcc_relocate(s1, NULL);
- if (ret < 0)
- return -1;
- ptr = tcc_malloc(ret);
- tcc_relocate(s1, ptr);
-
- prog_main = tcc_get_symbol_err(s1, "main");
-
- if (s1->do_debug) {
-#ifdef CONFIG_TCC_BACKTRACE
- struct sigaction sigact;
- /* install TCC signal handlers to print debug info on fatal
- runtime errors */
- sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
- 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);
-#else
- error("debug mode not available");
-#endif
- }
-
-#ifdef CONFIG_TCC_BCHECK
- if (s1->do_bounds_check) {
- void (*bound_init)(void);
- void (*bound_exit)(void);
- /* set error function */
- rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
- rt_prog_main = (unsigned long)prog_main;
- /* XXX: use .init section so that it also work in binary ? */
- bound_init = tcc_get_symbol_err(s1, "__bound_init");
- bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
- bound_init();
- ret = (*prog_main)(argc, argv);
- bound_exit();
- } else
-#endif
- ret = (*prog_main)(argc, argv);
- tcc_free(ptr);
- return ret;
-}
-
-void tcc_memstats(void)
-{
-#ifdef MEM_DEBUG
- printf("memory in use: %d\n", mem_cur_size);
-#endif
-}
-
static void tcc_cleanup(void)
{
int i, n;
diff --git a/tccrun.c b/tccrun.c
new file mode 100644
index 0000000..c321c1c
--- /dev/null
+++ b/tccrun.c
@@ -0,0 +1,518 @@
+/*
+ * TCC - Tiny C Compiler
+ *
+ * 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
+ */
+
+/* tccrun.c - support for tcc -run */
+
+
+/********************************************************/
+#ifdef CONFIG_TCC_STATIC
+
+#define RTLD_LAZY 0x001
+#define RTLD_NOW 0x002
+#define RTLD_GLOBAL 0x100
+#define RTLD_DEFAULT NULL
+
+/* dummy function for profiling */
+void *dlopen(const char *filename, int flag)
+{
+ return NULL;
+}
+
+void dlclose(void *p)
+{
+}
+
+const char *dlerror(void)
+{
+ return "error";
+}
+
+typedef struct TCCSyms {
+ char *str;
+ void *ptr;
+} TCCSyms;
+
+#define TCCSYM(a) { #a, &a, },
+
+/* add the symbol you want here if no dynamic linking is done */
+static TCCSyms tcc_syms[] = {
+#if !defined(CONFIG_TCCBOOT)
+ TCCSYM(printf)
+ TCCSYM(fprintf)
+ TCCSYM(fopen)
+ TCCSYM(fclose)
+#endif
+ { NULL, NULL },
+};
+
+void *resolve_sym(TCCState *s1, const char *symbol)
+{
+ TCCSyms *p;
+ p = tcc_syms;
+ while (p->str != NULL) {
+ if (!strcmp(p->str, symbol))
+ return p->ptr;
+ p++;
+ }
+ return NULL;
+}
+
+#elif defined(_WIN32)
+#define dlclose FreeLibrary
+
+#else
+#include <dlfcn.h>
+
+void *resolve_sym(TCCState *s1, const char *sym)
+{
+ return dlsym(RTLD_DEFAULT, sym);
+}
+
+#endif /* defined CONFIG_TCC_STATIC
+
+/********************************************************/
+
+#ifdef CONFIG_TCC_BACKTRACE
+
+/* print the position in the source file of PC value 'pc' by reading
+ the stabs debug information */
+static unsigned long rt_printline(unsigned long wanted_pc)
+{
+ Stab_Sym *sym, *sym_end;
+ char func_name[128], last_func_name[128];
+ unsigned long func_addr, last_pc, pc;
+ const char *incl_files[INCLUDE_STACK_SIZE];
+ int incl_index, len, last_line_num, i;
+ const char *str, *p;
+
+ fprintf(stderr, "0x%08lx:", wanted_pc);
+
+ func_name[0] = '\0';
+ func_addr = 0;
+ incl_index = 0;
+ last_func_name[0] = '\0';
+ last_pc = 0xffffffff;
+ last_line_num = 1;
+ sym = (Stab_Sym *)stab_section->data + 1;
+ sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset);
+ while (sym < sym_end) {
+ switch(sym->n_type) {
+ /* function start or end */
+ case N_FUN:
+ if (sym->n_strx == 0) {
+ /* we test if between last line and end of function */
+ pc = sym->n_value + func_addr;
+ if (wanted_pc >= last_pc && wanted_pc < pc)
+ goto found;
+ func_name[0] = '\0';
+ func_addr = 0;
+ } else {
+ str = stabstr_section->data + sym->n_strx;
+ p = strchr(str, ':');
+ if (!p) {
+ pstrcpy(func_name, sizeof(func_name), str);
+ } else {
+ len = p - str;
+ if (len > sizeof(func_name) - 1)
+ len = sizeof(func_name) - 1;
+ memcpy(func_name, str, len);
+ func_name[len] = '\0';
+ }
+ func_addr = sym->n_value;
+ }
+ break;
+ /* line number info */
+ case N_SLINE:
+ pc = sym->n_value + func_addr;
+ if (wanted_pc >= last_pc && wanted_pc < pc)
+ goto found;
+ last_pc = pc;
+ last_line_num = sym->n_desc;
+ /* XXX: slow! */
+ strcpy(last_func_name, func_name);
+ break;
+ /* include files */
+ case N_BINCL:
+ str = stabstr_section->data + sym->n_strx;
+ add_incl:
+ if (incl_index < INCLUDE_STACK_SIZE) {
+ incl_files[incl_index++] = str;
+ }
+ break;
+ case N_EINCL:
+ if (incl_index > 1)
+ incl_index--;
+ break;
+ case N_SO:
+ if (sym->n_strx == 0) {
+ incl_index = 0; /* end of translation unit */
+ } else {
+ str = stabstr_section->data + sym->n_strx;
+ /* do not add path */
+ len = strlen(str);
+ if (len > 0 && str[len - 1] != '/')
+ goto add_incl;
+ }
+ break;
+ }
+ sym++;
+ }
+
+ /* second pass: we try symtab symbols (no line number info) */
+ incl_index = 0;
+ {
+ ElfW(Sym) *sym, *sym_end;
+ int type;
+
+ sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
+ for(sym = (ElfW(Sym) *)symtab_section->data + 1;
+ sym < sym_end;
+ sym++) {
+ type = ELFW(ST_TYPE)(sym->st_info);
+ if (type == STT_FUNC) {
+ if (wanted_pc >= sym->st_value &&
+ wanted_pc < sym->st_value + sym->st_size) {
+ pstrcpy(last_func_name, sizeof(last_func_name),
+ strtab_section->data + sym->st_name);
+ func_addr = sym->st_value;
+ goto found;
+ }
+ }
+ }
+ }
+ /* did not find any info: */
+ fprintf(stderr, " ???\n");
+ return 0;
+ found:
+ if (last_func_name[0] != '\0') {
+ fprintf(stderr, " %s()", last_func_name);
+ }
+ if (incl_index > 0) {
+ fprintf(stderr, " (%s:%d",
+ incl_files[incl_index - 1], last_line_num);
+ for(i = incl_index - 2; i >= 0; i--)
+ fprintf(stderr, ", included from %s", incl_files[i]);
+ fprintf(stderr, ")");
+ }
+ fprintf(stderr, "\n");
+ return func_addr;
+}
+
+#ifdef __i386__
+/* fix for glibc 2.1 */
+#ifndef REG_EIP
+#define REG_EIP EIP
+#define REG_EBP EBP
+#endif
+
+/* return the PC at frame level 'level'. Return non zero if not found */
+static int rt_get_caller_pc(unsigned long *paddr,
+ ucontext_t *uc, int level)
+{
+ unsigned long fp;
+ int i;
+
+ if (level == 0) {
+#if defined(__FreeBSD__)
+ *paddr = uc->uc_mcontext.mc_eip;
+#elif defined(__dietlibc__)
+ *paddr = uc->uc_mcontext.eip;
+#else
+ *paddr = uc->uc_mcontext.gregs[REG_EIP];
+#endif
+ return 0;
+ } else {
+#if defined(__FreeBSD__)
+ fp = uc->uc_mcontext.mc_ebp;
+#elif defined(__dietlibc__)
+ fp = uc->uc_mcontext.ebp;
+#else
+ fp = uc->uc_mcontext.gregs[REG_EBP];
+#endif
+ for(i=1;i<level;i++) {
+ /* XXX: check address validity with program info */
+ if (fp <= 0x1000 || fp >= 0xc0000000)
+ return -1;
+ fp = ((unsigned long *)fp)[0];
+ }
+ *paddr = ((unsigned long *)fp)[1];
+ return 0;
+ }
+}
+#elif defined(__x86_64__)
+/* return the PC at frame level 'level'. Return non zero if not found */
+static int rt_get_caller_pc(unsigned long *paddr,
+ ucontext_t *uc, int level)
+{
+ unsigned long fp;
+ int i;
+
+ if (level == 0) {
+ /* XXX: only support linux */
+#if defined(__FreeBSD__)
+ *paddr = uc->uc_mcontext.mc_rip;
+#else
+ *paddr = uc->uc_mcontext.gregs[REG_RIP];
+#endif
+ return 0;
+ } else {
+#if defined(__FreeBSD__)
+ fp = uc->uc_mcontext.mc_rbp;
+#else
+ fp = uc->uc_mcontext.gregs[REG_RBP];
+#endif
+ for(i=1;i<level;i++) {
+ /* XXX: check address validity with program info */
+ if (fp <= 0x1000)
+ return -1;
+ fp = ((unsigned long *)fp)[0];
+ }
+ *paddr = ((unsigned long *)fp)[1];
+ return 0;
+ }
+}
+#else
+#warning add arch specific rt_get_caller_pc()
+static int rt_get_caller_pc(unsigned long *paddr,
+ ucontext_t *uc, int level)
+{
+ return -1;
+}
+#endif
+
+/* emit a run time error at position 'pc' */
+void rt_error(ucontext_t *uc, const char *fmt, ...)
+{
+ va_list ap;
+ unsigned long pc;
+ int i;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "Runtime error: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ for(i=0;i<num_callers;i++) {
+ if (rt_get_caller_pc(&pc, uc, i) < 0)
+ break;
+ if (i == 0)
+ fprintf(stderr, "at ");
+ else
+ fprintf(stderr, "by ");
+ pc = rt_printline(pc);
+ if (pc == rt_prog_main && pc)
+ break;
+ }
+ exit(255);
+ va_end(ap);
+}
+
+/* signal handler for fatal errors */
+static void sig_error(int signum, siginfo_t *siginf, void *puc)
+{
+ ucontext_t *uc = puc;
+
+ switch(signum) {
+ case SIGFPE:
+ switch(siginf->si_code) {
+ case FPE_INTDIV:
+ case FPE_FLTDIV:
+ rt_error(uc, "division by zero");
+ break;
+ default:
+ rt_error(uc, "floating point exception");
+ break;
+ }
+ break;
+ case SIGBUS:
+ case SIGSEGV:
+ if (rt_bound_error_msg && *rt_bound_error_msg)
+ rt_error(uc, *rt_bound_error_msg);
+ else
+ rt_error(uc, "dereferencing invalid pointer");
+ break;
+ case SIGILL:
+ rt_error(uc, "illegal instruction");
+ break;
+ case SIGABRT:
+ rt_error(uc, "abort() called");
+ break;
+ default:
+ rt_error(uc, "caught signal %d", signum);
+ break;
+ }
+ exit(255);
+}
+
+#endif /* defined CONFIG_TCC_BACKTRACE */
+
+/********************************************************/
+
+void set_pages_executable(void *ptr, unsigned long length)
+{
+#ifdef _WIN32
+ unsigned long old_protect;
+ VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
+#else
+ unsigned long start, end;
+ start = (unsigned long)ptr & ~(PAGESIZE - 1);
+ end = (unsigned long)ptr + length;
+ end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
+ mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
+#endif
+}
+
+/* copy code into memory passed in by the caller and do all relocations
+ (needed before using tcc_get_symbol()).
+ returns -1 on error and required size if ptr is NULL */
+int tcc_relocate(TCCState *s1, void *ptr)
+{
+ Section *s;
+ unsigned long offset, length;
+ uplong mem;
+ int i;
+
+ if (0 == s1->runtime_added) {
+ s1->runtime_added = 1;
+ s1->nb_errors = 0;
+#ifdef TCC_TARGET_PE
+ pe_output_file(s1, NULL);
+#else
+ tcc_add_runtime(s1);
+ relocate_common_syms();
+ tcc_add_linker_symbols(s1);
+ build_got_entries(s1);
+#endif
+ if (s1->nb_errors)
+ return -1;
+ }
+
+ offset = 0, mem = (uplong)ptr;
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (0 == (s->sh_flags & SHF_ALLOC))
+ continue;
+ length = s->data_offset;
+ s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0;
+ offset = (offset + length + 15) & ~15;
+ }
+
+ /* relocate symbols */
+ relocate_syms(s1, 1);
+ if (s1->nb_errors)
+ return -1;
+
+#ifndef TCC_TARGET_PE
+#ifdef TCC_TARGET_X86_64
+ s1->runtime_plt_and_got_offset = 0;
+ s1->runtime_plt_and_got = (char *)(mem + offset);
+ /* double the size of the buffer for got and plt entries
+ XXX: calculate exact size for them? */
+ offset *= 2;
+#endif
+#endif
+
+ if (0 == mem)
+ return offset + 15;
+
+ /* relocate each section */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (s->reloc)
+ relocate_section(s1, s);
+ }
+
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (0 == (s->sh_flags & SHF_ALLOC))
+ continue;
+ length = s->data_offset;
+ // printf("%-12s %08x %04x\n", s->name, s->sh_addr, length);
+ ptr = (void*)(uplong)s->sh_addr;
+ if (NULL == s->data || s->sh_type == SHT_NOBITS)
+ memset(ptr, 0, length);
+ else
+ memcpy(ptr, s->data, length);
+ /* mark executable sections as executable in memory */
+ if (s->sh_flags & SHF_EXECINSTR)
+ set_pages_executable(ptr, length);
+ }
+#ifndef TCC_TARGET_PE
+#ifdef TCC_TARGET_X86_64
+ set_pages_executable(s1->runtime_plt_and_got,
+ s1->runtime_plt_and_got_offset);
+#endif
+#endif
+ return 0;
+}
+
+/* launch the compiled program with the given arguments */
+int tcc_run(TCCState *s1, int argc, char **argv)
+{
+ int (*prog_main)(int, char **);
+ void *ptr;
+ int ret;
+
+ ret = tcc_relocate(s1, NULL);
+ if (ret < 0)
+ return -1;
+ ptr = tcc_malloc(ret);
+ tcc_relocate(s1, ptr);
+
+ prog_main = tcc_get_symbol_err(s1, "main");
+
+ if (s1->do_debug) {
+#ifdef CONFIG_TCC_BACKTRACE
+ struct sigaction sigact;
+ /* install TCC signal handlers to print debug info on fatal
+ runtime errors */
+ sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
+ 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);
+#else
+ error("debug mode not available");
+#endif
+ }
+
+#ifdef CONFIG_TCC_BCHECK
+ if (s1->do_bounds_check) {
+ void (*bound_init)(void);
+ void (*bound_exit)(void);
+ /* set error function */
+ rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
+ rt_prog_main = (unsigned long)prog_main;
+ /* XXX: use .init section so that it also work in binary ? */
+ bound_init = tcc_get_symbol_err(s1, "__bound_init");
+ bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
+ bound_init();
+ ret = (*prog_main)(argc, argv);
+ bound_exit();
+ } else
+#endif
+ ret = (*prog_main)(argc, argv);
+ tcc_free(ptr);
+ return ret;
+}
+
+/********************************************************/