From 3db219477a6365d0f1d5214a345b3393a38e81d1 Mon Sep 17 00:00:00 2001 From: grischka Date: Sat, 19 Dec 2009 22:10:26 +0100 Subject: tccrun: new file factor out -run support --- tccrun.c | 518 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 tccrun.c (limited to 'tccrun.c') 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 + +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= 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;isi_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; +} + +/********************************************************/ -- cgit v1.3.1