tccrun: new file

factor out -run support
master
grischka 2009-12-19 22:10:26 +01:00
parent 41e112360f
commit 3db219477a
2 changed files with 527 additions and 497 deletions

506
libtcc.c
View File

@ -352,6 +352,8 @@ static void asm_global_instr(void)
#include "tccpe.c"
#endif
#include "tccrun.c"
/********************************************************/
#ifdef _WIN32
char *normalize_slashes(char *path)
@ -389,72 +391,6 @@ BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
#endif
#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
@ -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;

518
tccrun.c 100644
View File

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