Alien-TinyCCx
view release on metacpan or search on metacpan
src/tccrun.c view on Meta::CPAN
/*
* 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
#ifndef _WIN32
# include <sys/mman.h>
#endif
#ifdef CONFIG_TCC_BACKTRACE
# ifndef _WIN32
# include <signal.h>
# ifndef __OpenBSD__
# include <sys/ucontext.h>
# endif
# else
# define ucontext_t CONTEXT
# endif
ST_DATA int rt_num_callers = 6;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
static void rt_error(ucontext_t *uc, const char *fmt, ...);
static void set_exception_handler(void);
#endif
static void set_pages_executable(void *ptr, unsigned long length);
static int tcc_relocate_ex(TCCState *s1, void *ptr);
#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1);
static void win64_del_function_table(void *);
#endif
// #define HAVE_SELINUX
/* ------------------------------------------------------------- */
/* Do all relocations (needed before using tcc_get_symbol())
Returns -1 on error. */
LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
{
int size; void *mem;
if (TCC_RELOCATE_AUTO != ptr)
return tcc_relocate_ex(s1, ptr);
size = tcc_relocate_ex(s1, NULL);
if (size < 0)
return -1;
#ifdef HAVE_SELINUX
{ /* Use mmap instead of malloc for Selinux. Ref:
http://www.gnu.org/s/libc/manual/html_node/File-Size.html */
char tmpfname[] = "/tmp/.tccrunXXXXXX";
int fd = mkstemp (tmpfname);
void *wr_mem;
unlink (tmpfname);
ftruncate (fd, size);
wr_mem = mmap (NULL, size, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
if (wr_mem == MAP_FAILED)
tcc_error("/tmp not writeable");
mem = mmap (NULL, size, PROT_READ|PROT_EXEC,
MAP_SHARED, fd, 0);
if (mem == MAP_FAILED)
tcc_error("/tmp not executable");
tcc_relocate_ex(s1, wr_mem);
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, wr_mem);
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, mem);
}
#else
mem = tcc_malloc(size);
tcc_relocate_ex(s1, mem); /* no more errors expected */
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, mem);
#endif
return 0;
}
ST_FUNC void tcc_run_free(TCCState *s1)
{
int i;
for (i = 0; i < s1->nb_runtime_mem; ++i) {
#ifdef HAVE_SELINUX
int size = (int)(addr_t)s1->runtime_mem[i];
munmap(s1->runtime_mem[++i], size);
munmap(s1->runtime_mem[++i], size);
#else
# ifdef _WIN64
win64_del_function_table(*(void**)s1->runtime_mem[i]);
# endif
tcc_free(s1->runtime_mem[i]);
#endif
}
tcc_free(s1->runtime_mem);
}
/* launch the compiled program with the given arguments */
LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
{
int (*prog_main)(int, char **);
if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
return -1;
prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
#ifdef CONFIG_TCC_BACKTRACE
if (s1->do_debug) {
set_exception_handler();
rt_prog_main = prog_main;
}
#endif
errno = 0; /* clean errno value */
#ifdef CONFIG_TCC_BCHECK
if (s1->do_bounds_check) {
void (*bound_init)(void);
void (*bound_exit)(void);
void (*bound_new_region)(void *p, addr_t size);
int (*bound_delete_region)(void *p);
int i, ret;
/* set error function */
rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
/* 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_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");
bound_init();
/* mark argv area as valid */
bound_new_region(argv, argc*sizeof(argv[0]));
for (i=0; i<argc; ++i)
bound_new_region(argv[i], strlen(argv[i]) + 1);
ret = (*prog_main)(argc, argv);
/* unmark argv area */
for (i=0; i<argc; ++i)
bound_delete_region(argv[i]);
bound_delete_region(argv);
bound_exit();
return ret;
}
#endif
return (*prog_main)(argc, argv);
}
/* 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)
{
Section *s;
unsigned long offset, length;
addr_t mem;
int i;
if (NULL == ptr) {
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 = (addr_t)ptr;
#ifdef _WIN64
offset += sizeof (void*);
#endif
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (0 == (s->sh_flags & SHF_ALLOC))
continue;
offset = (offset + 15) & ~15;
s->sh_addr = mem ? mem + offset : 0;
offset += s->data_offset;
}
/* relocate symbols */
relocate_syms(s1, 1);
if (s1->nb_errors)
return -1;
if (0 == mem)
return offset;
/* relocate each section */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
if (s->reloc)
relocate_section(s1, s);
}
relocate_plt(s1);
#ifdef _WIN64
*(void**)ptr = win64_add_function_table(s1);
#endif
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 %08lx %04x\n", s->name, s->sh_addr, length);
ptr = (void*)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);
}
/* #ifdef CONFIG_TCC_EXSYMTAB */
/* If they have an extended symbol table, copy the symbol pointers. */
if (s1->exsymtab > (extended_symtab*)1) copy_extended_symbols_to_exsymtab(s1);
/* #endif */
return 0;
}
/* ------------------------------------------------------------- */
/* allow to run code in memory */
static void set_pages_executable(void *ptr, unsigned long length)
{
#ifdef _WIN32
unsigned long old_protect;
VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
#else
#ifndef PAGESIZE
# define PAGESIZE 4096
#endif
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, PROT_READ | PROT_WRITE | PROT_EXEC))
tcc_error("mprotect failed: did you mean to configure --with-selinux?");
#if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
{ extern void __clear_cache(void *beginning, void *end);
__clear_cache(ptr, (char *)ptr + length); }
#endif
#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;
( run in 1.182 second using v1.01-cache-2.11-cpan-5511b514fd6 )