LIBTCCAPI tcc_relocate(s) : REMOVED 2nd argument

removed second argument for tcc_relocate(s). previous
'TCC_RELOCATE_AUTO' is now default and only behavior.

Rationale:
  In the past, the option to compile into memory provided by the
  user was introduced because only one TCCState could exist at a time.

  This is no longer a limitation.  As such it is also possible now to
  keep any number of compiled code snippets around together with their
  state in order to run them as needed.

- Also
  - LIBTCCAPI tcc_get_error_func/opaque() removed
  - tccrun/SELINUX: switch rx/rw mappings such that rx comes first
    (risc64-link.c:relocate_plt() does not like got < plt)
  - tcc_relocate_ex(): free local symbols and obsolete sections
    to reduce memory after tcc_relocate()
This commit is contained in:
grischka 2024-02-07 07:42:56 +01:00
parent a0ab99169e
commit b671fc0594
11 changed files with 224 additions and 184 deletions

14
configure vendored
View file

@ -56,6 +56,7 @@ build_cross=
# use CC/AR from environment when set # use CC/AR from environment when set
test -n "$CC" && cc="$CC" test -n "$CC" && cc="$CC"
test -n "$AR" && ar="$AR" test -n "$AR" && ar="$AR"
test -n "CFLAGS" && CFLAGS="-Wall -O2"
# find source path # find source path
source_path=${0%configure} source_path=${0%configure}
@ -399,14 +400,6 @@ if test "$mingw32" = "no"; then
default infodir "${sharedir}/info" default infodir "${sharedir}/info"
fi fi
# set default CFLAGS
default CFLAGS "-Wall -O2"
if test "$mingw32" = "yes" -a "$cc_name" = "gcc"; then
# avoid mingw dependencies such as 'libgcc_s_dw2-1.dll'
default LDFLAGS "-static"
fi
if test x"$show_help" = "xyes" ; then if test x"$show_help" = "xyes" ; then
show_help show_help
fi fi
@ -464,6 +457,11 @@ if test "$bigendian" = "yes" ; then
confvars="$confvars BIGENDIAN" confvars="$confvars BIGENDIAN"
fi fi
if test "$mingw32" = "yes" -a "$cc_name" = "gcc"; then
# avoid mingw dependencies such as 'libgcc_s_dw2-1.dll'
default LDFLAGS "-static"
fi
if test "$cpu" = "arm"; then if test "$cpu" = "arm"; then
if test "${triplet%eabihf}" != "$triplet" ; then if test "${triplet%eabihf}" != "$triplet" ; then
confvars="$confvars arm_eabihf arm_vfp" confvars="$confvars arm_eabihf arm_vfp"

View file

@ -249,7 +249,7 @@ ST_FUNC char *tcc_load_text(int fd)
#undef free #undef free
#undef realloc #undef realloc
static void *default_reallocator(void *ptr, size_t size) static void *default_reallocator(void *ptr, unsigned long size)
{ {
void *ptr1; void *ptr1;
if (size == 0) { if (size == 0) {
@ -275,18 +275,13 @@ static void libc_free(void *ptr)
#define realloc(p, s) use_tcc_realloc(p, s) #define realloc(p, s) use_tcc_realloc(p, s)
/* global so that every tcc_alloc()/tcc_free() call doesn't need to be changed */ /* global so that every tcc_alloc()/tcc_free() call doesn't need to be changed */
static TCCReallocFunc reallocator = default_reallocator; static void *(*reallocator)(void*, unsigned long) = default_reallocator;
LIBTCCAPI void tcc_set_realloc(TCCReallocFunc realloc) LIBTCCAPI void tcc_set_realloc(TCCReallocFunc *realloc)
{ {
reallocator = realloc; reallocator = realloc;
} }
LIBTCCAPI TCCReallocFunc tcc_get_realloc()
{
return reallocator;
}
/* in case MEM_DEBUG is #defined */ /* in case MEM_DEBUG is #defined */
#undef tcc_free #undef tcc_free
#undef tcc_malloc #undef tcc_malloc
@ -646,7 +641,7 @@ static void error1(int mode, const char *fmt, va_list ap)
} }
cstr_printf(&cs, mode == ERROR_WARN ? "warning: " : "error: "); cstr_printf(&cs, mode == ERROR_WARN ? "warning: " : "error: ");
cstr_vprintf(&cs, fmt, ap); cstr_vprintf(&cs, fmt, ap);
if (!s1 || !s1->error_func) { if (!s1->error_func) {
/* default case: stderr */ /* default case: stderr */
if (s1 && s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout) if (s1 && s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout)
printf("\n"); /* print a newline during tcc -E */ printf("\n"); /* print a newline during tcc -E */
@ -666,22 +661,12 @@ static void error1(int mode, const char *fmt, va_list ap)
} }
} }
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc error_func) LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc *error_func)
{ {
s->error_opaque = error_opaque; s->error_opaque = error_opaque;
s->error_func = error_func; s->error_func = error_func;
} }
LIBTCCAPI TCCErrorFunc tcc_get_error_func(TCCState *s)
{
return s->error_func;
}
LIBTCCAPI void *tcc_get_error_opaque(TCCState *s)
{
return s->error_opaque;
}
/* error without aborting current compilation */ /* error without aborting current compilation */
PUB_FUNC int _tcc_error_noabort(const char *fmt, ...) PUB_FUNC int _tcc_error_noabort(const char *fmt, ...)
{ {
@ -2245,6 +2230,15 @@ PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time)
s1->total_output[3] s1->total_output[3]
); );
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
fprintf(stderr, "# %d bytes memory used\n", mem_max_size); fprintf(stderr, "# memory usage");
#ifdef TCC_IS_NATIVE
if (s1->run_size) {
Section *s = s1->symtab;
int ms = s->data_offset + s->link->data_offset + s->hash->data_offset;
fprintf(stderr, ": %d to run, %d symbols, %d other,",
s1->run_size, ms, mem_cur_size - s1->run_size - ms);
}
#endif
fprintf(stderr, " %d max (bytes)\n", mem_max_size);
#endif #endif
} }

View file

@ -9,19 +9,16 @@
extern "C" { extern "C" {
#endif #endif
struct TCCState; /*****************************/
/* set custom allocator for all allocations (optional) */
typedef void *TCCReallocFunc(void *ptr, unsigned long size);
LIBTCCAPI void tcc_set_realloc(TCCReallocFunc *my_realloc);
/*****************************/
typedef struct TCCState TCCState; typedef struct TCCState TCCState;
typedef void (*TCCErrorFunc)(void *opaque, const char *msg);
typedef void *(*TCCReallocFunc)(void *ptr, size_t size);
/* to be used for all allocation (including tcc_new()), otherwise malloc(), realloc(), free() */
LIBTCCAPI void tcc_set_realloc(TCCReallocFunc realloc);
/* return current allocator */
LIBTCCAPI TCCReallocFunc tcc_get_realloc();
/* create a new TCC compilation context */ /* create a new TCC compilation context */
LIBTCCAPI TCCState *tcc_new(void); LIBTCCAPI TCCState *tcc_new(void);
@ -31,14 +28,9 @@ LIBTCCAPI void tcc_delete(TCCState *s);
/* set CONFIG_TCCDIR at runtime */ /* set CONFIG_TCCDIR at runtime */
LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path);
/* set error/warning display callback */ /* set error/warning callback (optional) */
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc error_func); typedef void TCCErrorFunc(void *opaque, const char *msg);
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc *error_func);
/* return error/warning callback */
LIBTCCAPI TCCErrorFunc tcc_get_error_func(TCCState *s);
/* return error/warning callback opaque pointer */
LIBTCCAPI void *tcc_get_error_opaque(TCCState *s);
/* set options as from command line (multiple supported) */ /* set options as from command line (multiple supported) */
LIBTCCAPI int tcc_set_options(TCCState *s, const char *str); LIBTCCAPI int tcc_set_options(TCCState *s, const char *str);
@ -67,6 +59,9 @@ LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename);
/* compile a string containing a C source. Return -1 if error. */ /* compile a string containing a C source. Return -1 if error. */
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf); LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf);
/* Tip: to have more specific errors/warnings from tcc_compile_string(),
you can prefix the string with "#line <num> \"<filename>\"\n" */
/*****************************/ /*****************************/
/* linking commands */ /* linking commands */
@ -96,18 +91,12 @@ LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename);
LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv); LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv);
/* do all relocations (needed before using tcc_get_symbol()) */ /* do all relocations (needed before using tcc_get_symbol()) */
LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr); LIBTCCAPI int tcc_relocate(TCCState *s1);
/* possible values for 'ptr':
- TCC_RELOCATE_AUTO : Allocate and manage memory internally
- NULL : return required memory size for the step below
- memory address : copy code to memory passed by the caller
returns -1 if error. */
#define TCC_RELOCATE_AUTO (void*)1
/* return symbol value or NULL if not found */ /* return symbol value or NULL if not found */
LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name); LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name);
/* return symbol value or NULL if not found */ /* list all (global) symbols and their values via 'symbol_cb()' */
LIBTCCAPI void tcc_list_symbols(TCCState *s, void *ctx, LIBTCCAPI void tcc_list_symbols(TCCState *s, void *ctx,
void (*symbol_cb)(void *ctx, const char *name, const void *val)); void (*symbol_cb)(void *ctx, const char *name, const void *val));

17
tcc.h
View file

@ -923,13 +923,11 @@ struct TCCState {
Section *lbounds_section; /* contains local data bound description */ Section *lbounds_section; /* contains local data bound description */
#endif #endif
/* symbol section */ /* symbol section */
Section *symtab_section; union { Section *symtab_section, *symtab; }; /* historical alias */
/* temporary dynamic symbol sections (for dll loading) */ /* temporary dynamic symbol sections (for dll loading) */
Section *dynsymtab_section; Section *dynsymtab_section;
/* exported dynamic symbol section */ /* exported dynamic symbol section */
Section *dynsym; Section *dynsym;
/* copy of the global symtab_section variable */
Section *symtab;
/* got & plt handling */ /* got & plt handling */
Section *got, *plt; Section *got, *plt;
/* debug sections */ /* debug sections */
@ -972,6 +970,8 @@ struct TCCState {
int uw_sym; int uw_sym;
unsigned uw_offs; unsigned uw_offs;
# endif # endif
#else
unsigned shf_RELRO; /* section flags for RELRO sections */
#endif #endif
#if defined TCC_TARGET_MACHO #if defined TCC_TARGET_MACHO
@ -991,9 +991,12 @@ struct TCCState {
#endif #endif
#ifdef TCC_IS_NATIVE #ifdef TCC_IS_NATIVE
const char *runtime_main; const char *run_main; /* entry for tcc_run() */
void **runtime_mem; void *run_ptr; /* ptr to runtime_memory */
int nb_runtime_mem; unsigned run_size; /* size of runtime_memory */
#ifdef _WIN64
void *run_function_table; /* unwind data */
#endif
#endif #endif
#ifdef CONFIG_TCC_BACKTRACE #ifdef CONFIG_TCC_BACKTRACE
@ -1542,7 +1545,9 @@ ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
ST_FUNC size_t section_add(Section *sec, addr_t size, int align); ST_FUNC size_t section_add(Section *sec, addr_t size, int align);
ST_FUNC void *section_ptr_add(Section *sec, addr_t size); ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
ST_FUNC Section *find_section(TCCState *s1, const char *name); ST_FUNC Section *find_section(TCCState *s1, const char *name);
ST_FUNC void free_section(Section *s);
ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags); ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);
ST_FUNC void init_symtab(Section *s);
ST_FUNC int put_elf_str(Section *s, const char *sym); ST_FUNC int put_elf_str(Section *s, const char *sym);
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name); ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);

View file

@ -714,9 +714,11 @@ ST_FUNC void tcc_debug_start(TCCState *s1)
char buf[512]; char buf[512];
char *filename; char *filename;
/* we might currently #include the <command-line> */
filename = file->prev ? file->prev->filename : file->filename;
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
symbols can be safely used */ symbols can be safely used */
filename = file->prev ? file->prev->filename : file->filename;
put_elf_sym(symtab_section, 0, 0, put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
SHN_ABS, filename); SHN_ABS, filename);

View file

@ -48,10 +48,10 @@ struct sym_version {
#define SHF_DYNSYM 0x40000000 #define SHF_DYNSYM 0x40000000
#ifdef TCC_TARGET_PE #ifdef TCC_TARGET_PE
static const int shf_RELRO = SHF_ALLOC; #define shf_RELRO SHF_ALLOC
static const char rdata[] = ".rdata"; static const char rdata[] = ".rdata";
#else #else
static const int shf_RELRO = SHF_ALLOC | SHF_WRITE; #define shf_RELRO s1->shf_RELRO
static const char rdata[] = ".data.ro"; static const char rdata[] = ".data.ro";
#endif #endif
@ -60,6 +60,13 @@ static const char rdata[] = ".data.ro";
ST_FUNC void tccelf_new(TCCState *s) ST_FUNC void tccelf_new(TCCState *s)
{ {
TCCState *s1 = s; TCCState *s1 = s;
#ifndef TCC_TARGET_PE
shf_RELRO = SHF_ALLOC;
if (s1->output_type != TCC_OUTPUT_MEMORY)
shf_RELRO |= SHF_WRITE; /* the ELF loader will set it to RO at runtime */
#endif
/* no section zero */ /* no section zero */
dynarray_add(&s->sections, &s->nb_sections, NULL); dynarray_add(&s->sections, &s->nb_sections, NULL);
@ -76,7 +83,6 @@ ST_FUNC void tccelf_new(TCCState *s)
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0, symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
".strtab", ".strtab",
".hashtab", SHF_PRIVATE); ".hashtab", SHF_PRIVATE);
s->symtab = symtab_section;
/* private symbol table for dynamic symbols */ /* private symbol table for dynamic symbols */
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE|SHF_DYNSYM, s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE|SHF_DYNSYM,
@ -99,9 +105,13 @@ ST_FUNC void tccelf_new(TCCState *s)
#endif #endif
} }
static void free_section(Section *s) ST_FUNC void free_section(Section *s)
{ {
if (!s)
return;
tcc_free(s->data); tcc_free(s->data);
s->data = NULL;
s->data_allocated = s->data_offset = 0;
} }
ST_FUNC void tccelf_delete(TCCState *s1) ST_FUNC void tccelf_delete(TCCState *s1)
@ -127,6 +137,9 @@ ST_FUNC void tccelf_delete(TCCState *s1)
free_section(s1->priv_sections[i]); free_section(s1->priv_sections[i]);
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections); dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
tcc_free(s1->sym_attrs);
symtab_section = NULL; /* for tccrun.c:rt_printline() */
/* free any loaded DLLs */ /* free any loaded DLLs */
#ifdef TCC_IS_NATIVE #ifdef TCC_IS_NATIVE
for ( i = 0; i < s1->nb_loaded_dlls; i++) { for ( i = 0; i < s1->nb_loaded_dlls; i++) {
@ -141,9 +154,6 @@ ST_FUNC void tccelf_delete(TCCState *s1)
#endif #endif
/* free loaded dlls array */ /* free loaded dlls array */
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls); dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
tcc_free(s1->sym_attrs);
symtab_section = NULL; /* for tccrun.c:rt_printline() */
} }
/* save section data state */ /* save section data state */
@ -261,32 +271,32 @@ ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh
return sec; return sec;
} }
ST_FUNC void init_symtab(Section *s)
{
int *ptr, nb_buckets = 1;
put_elf_str(s->link, "");
section_ptr_add(s, sizeof (ElfW(Sym)));
ptr = section_ptr_add(s->hash, (2 + nb_buckets + 1) * sizeof(int));
ptr[0] = nb_buckets;
ptr[1] = 1;
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
}
ST_FUNC Section *new_symtab(TCCState *s1, ST_FUNC Section *new_symtab(TCCState *s1,
const char *symtab_name, int sh_type, int sh_flags, const char *symtab_name, int sh_type, int sh_flags,
const char *strtab_name, const char *strtab_name,
const char *hash_name, int hash_sh_flags) const char *hash_name, int hash_sh_flags)
{ {
Section *symtab, *strtab, *hash; Section *symtab, *strtab, *hash;
int *ptr, nb_buckets;
symtab = new_section(s1, symtab_name, sh_type, sh_flags); symtab = new_section(s1, symtab_name, sh_type, sh_flags);
symtab->sh_entsize = sizeof(ElfW(Sym)); symtab->sh_entsize = sizeof(ElfW(Sym));
strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags); strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
put_elf_str(strtab, "");
symtab->link = strtab; symtab->link = strtab;
put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
nb_buckets = 1;
hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags); hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
hash->sh_entsize = sizeof(int); hash->sh_entsize = sizeof(int);
symtab->hash = hash; symtab->hash = hash;
hash->link = symtab; hash->link = symtab;
init_symtab(symtab);
ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
ptr[0] = nb_buckets;
ptr[1] = 1;
memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
return symtab; return symtab;
} }

View file

@ -1349,7 +1349,7 @@ static int pe_check_symbols(struct pe_info *pe)
sprintf(buffer, "IAT.%s", name); sprintf(buffer, "IAT.%s", name);
is->iat_index = put_elf_sym( is->iat_index = put_elf_sym(
symtab_section, 0, sizeof(DWORD), symtab_section, 0, sizeof(DWORD),
ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), ELFW(ST_INFO)(STB_LOCAL, STT_OBJECT),
0, SHN_UNDEF, buffer); 0, SHN_UNDEF, buffer);
offset = text_section->data_offset; offset = text_section->data_offset;
@ -1970,7 +1970,7 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
/* grab the startup code from libtcc1.a */ /* grab the startup code from libtcc1.a */
#ifdef TCC_IS_NATIVE #ifdef TCC_IS_NATIVE
if (TCC_OUTPUT_MEMORY != s1->output_type || s1->runtime_main) if (TCC_OUTPUT_MEMORY != s1->output_type || s1->run_main)
#endif #endif
set_global_sym(s1, start_symbol, NULL, 0); set_global_sym(s1, start_symbol, NULL, 0);
@ -2078,7 +2078,7 @@ ST_FUNC int pe_output_file(TCCState *s1, const char *filename)
#ifdef TCC_IS_NATIVE #ifdef TCC_IS_NATIVE
pe.thunk = data_section; pe.thunk = data_section;
pe_build_imports(&pe); pe_build_imports(&pe);
s1->runtime_main = pe.start_symbol; s1->run_main = pe.start_symbol;
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
s1->uw_pdata = find_section(s1, ".pdata"); s1->uw_pdata = find_section(s1, ".pdata");
#endif #endif

216
tccrun.c
View file

@ -76,8 +76,8 @@ static int _rt_error(void *fp, void *ip, const char *fmt, va_list ap);
# include <sys/mman.h> # include <sys/mman.h>
#endif #endif
static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length); static int protect_pages(void *ptr, unsigned long length, int mode);
static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff); static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff);
#ifdef _WIN64 #ifdef _WIN64
static void *win64_add_function_table(TCCState *s1); static void *win64_add_function_table(TCCState *s1);
@ -88,13 +88,11 @@ static void win64_del_function_table(void *);
/* Do all relocations (needed before using tcc_get_symbol()) /* Do all relocations (needed before using tcc_get_symbol())
Returns -1 on error. */ Returns -1 on error. */
LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr) LIBTCCAPI int tcc_relocate(TCCState *s1)
{ {
void *ptr;
int size; int size;
addr_t ptr_diff = 0; unsigned ptr_diff = 0;
if (TCC_RELOCATE_AUTO != ptr)
return tcc_relocate_ex(s1, ptr, 0);
size = tcc_relocate_ex(s1, NULL, 0); size = tcc_relocate_ex(s1, NULL, 0);
if (size < 0) if (size < 0)
@ -103,51 +101,45 @@ LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
{ {
/* Using mmap instead of malloc */ /* Using mmap instead of malloc */
void *prx; void *prw;
char tmpfname[] = "/tmp/.tccrunXXXXXX"; char tmpfname[] = "/tmp/.tccrunXXXXXX";
int fd = mkstemp(tmpfname); int fd = mkstemp(tmpfname);
unlink(tmpfname); unlink(tmpfname);
ftruncate(fd, size); ftruncate(fd, size);
size = (size + (PAGESIZE-1)) & ~(PAGESIZE-1); ptr = mmap(NULL, size * 2, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
ptr = mmap(NULL, size * 2, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); /* mmap RW memory at fixed distance */
/* mmap RX memory at a fixed distance */ prw = mmap((char*)ptr + size, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, fd, 0);
prx = mmap((char*)ptr + size, size, PROT_READ|PROT_EXEC, MAP_SHARED|MAP_FIXED, fd, 0);
close(fd); close(fd);
if (ptr == MAP_FAILED || prx == MAP_FAILED) if (ptr == MAP_FAILED || prw == MAP_FAILED)
return tcc_error_noabort("tccrun: could not map memory"); return tcc_error_noabort("tccrun: could not map memory");
ptr_diff = (char*)prx - (char*)ptr; ptr_diff = (char*)prw - (char*)ptr; /* = size; */
//printf("map %p %p %p\n", ptr, prx, (void*)ptr_diff); //printf("map %p %p %p\n", ptr, prw, (void*)ptr_diff);
} }
#else #else
ptr = tcc_malloc(size); ptr = tcc_malloc(size);
#endif #endif
if (tcc_relocate_ex(s1, ptr, ptr_diff)) s1->run_ptr = ptr;
return -1; s1->run_size = size;
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size); tcc_relocate_ex(s1, ptr, ptr_diff);
dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
return 0; return 0;
} }
ST_FUNC void tcc_run_free(TCCState *s1) ST_FUNC void tcc_run_free(TCCState *s1)
{ {
int i; void *ptr = s1->run_ptr;
unsigned size = s1->run_size;
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 #ifdef HAVE_SELINUX
munmap(ptr, size * 2); munmap(ptr, size * 2);
#else #else
/* unprotect memory to make it usable for malloc again */ /* unprotect memory to make it usable for malloc again */
set_pages_executable(s1, 2, ptr, size); protect_pages(ptr, size, 2 /*rw*/);
#ifdef _WIN64 #ifdef _WIN64
win64_del_function_table(*(void**)ptr); win64_del_function_table(s1->run_function_table);
#endif #endif
tcc_free(ptr); tcc_free(ptr);
#endif #endif
}
tcc_free(s1->runtime_mem);
} }
static void run_cdtors(TCCState *s1, const char *start, const char *end, static void run_cdtors(TCCState *s1, const char *start, const char *end,
@ -198,17 +190,17 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
char **envp = environ; char **envp = environ;
#endif #endif
s1->runtime_main = s1->nostdlib ? "_start" : "main"; s1->run_main = s1->nostdlib ? "_start" : "main";
if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->runtime_main, 0, 1)) if ((s1->dflag & 16) && (addr_t)-1 == get_sym_addr(s1, s1->run_main, 0, 1))
return 0; return 0;
tcc_add_symbol(s1, "exit", rt_exit); tcc_add_symbol(s1, "exit", rt_exit);
tcc_add_symbol(s1, "atexit", rt_atexit); tcc_add_symbol(s1, "atexit", rt_atexit);
tcc_add_symbol(s1, "on_exit", rt_on_exit); tcc_add_symbol(s1, "on_exit", rt_on_exit);
if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0) if (tcc_relocate(s1) < 0)
return -1; return -1;
prog_main = (void*)get_sym_addr(s1, s1->runtime_main, 1, 1); prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1);
if ((addr_t)-1 == (addr_t)prog_main) if ((addr_t)-1 == (addr_t)prog_main)
return -1; return -1;
@ -275,28 +267,62 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
return ret; return ret;
} }
#define DEBUG_RUNMEN 0 /* ------------------------------------------------------------- */
/* remove all STB_LOCAL symbols */
static void cleanup_symbols(TCCState *s1)
{
Section *s = s1->symtab;
int sym_index, end_sym = s->data_offset / sizeof (ElfSym);
/* reset symtab */
s->data_offset = s->link->data_offset = s->hash->data_offset = 0;
init_symtab(s);
/* re-add symbols except STB_LOCAL */
for (sym_index = 1; sym_index < end_sym; ++sym_index) {
ElfW(Sym) *sym = &((ElfW(Sym) *)s->data)[sym_index];
const char *name = (char *)s->link->data + sym->st_name;
if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
continue;
//printf("sym %s\n", name);
put_elf_sym(s, sym->st_value, sym->st_size, sym->st_info, sym->st_other, sym->st_shndx, name);
}
}
/* enable rx/ro/rw permissions */ /* free all sections except symbols */
#define CONFIG_RUNMEM_RO 1 static void cleanup_sections(TCCState *s1)
{
struct { Section **secs; int nb_secs; } *p = (void*)&s1->sections;
int i, f = 2;
do {
for (i = --f; i < p->nb_secs; i++) {
Section *s = p->secs[i];
if (s == s1->symtab || s == s1->symtab->link || s == s1->symtab->hash
|| 0 == memcmp(s->name, ".stab", 5)
|| 0 == memcmp(s->name, ".debug_", 7)) {
s->data = tcc_realloc(s->data, s->data_allocated = s->data_offset);
} else {
free_section(s);
if (0 == (s->sh_flags & SHF_ALLOC))
tcc_free(s), p->secs[i] = NULL;
}
}
} while (++p, f);
}
#if CONFIG_RUNMEM_RO /* ------------------------------------------------------------- */
# define PAGE_ALIGN PAGESIZE /* 0 = .text rwx other rw */
#elif defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 /* 1 = .text rx .rdata r .data/.bss rw */
/* To avoid that x86 processors would reload cached instructions #ifndef CONFIG_RUNMEM_RO
each time when data is written in the near, we need to make # define CONFIG_RUNMEM_RO 1
sure that code and data do not share the same 64 byte unit */
# define PAGE_ALIGN 64
#else
# define PAGE_ALIGN 1
#endif #endif
#define DEBUG_RUNMEN 0
/* relocate code. Return -1 on error, required size if ptr is NULL, /* relocate code. Return -1 on error, required size if ptr is NULL,
otherwise copy code into buffer passed by the caller */ otherwise copy code into buffer passed by the caller */
static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff) static int tcc_relocate_ex(TCCState *s1, void *ptr, unsigned ptr_diff)
{ {
Section *s; Section *s;
unsigned offset, length, align, max_align, i, k, f; unsigned offset, length, align, i, k, f;
unsigned n, copy; unsigned n, copy;
addr_t mem, addr; addr_t mem, addr;
@ -313,11 +339,15 @@ static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
return -1; return -1;
} }
offset = max_align = 0, mem = (addr_t)ptr; offset = copy = 0;
#ifdef _WIN64 mem = (addr_t)ptr;
offset += sizeof (void*); /* space for function_table pointer */
#if DEBUG_RUNMEN
if (mem)
fprintf(stderr, "X: <base> %p len %5x\n",
ptr, s1->run_size);
#endif #endif
copy = 0;
redo: redo:
for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */ for (k = 0; k < 3; ++k) { /* 0:rx, 1:ro, 2:rw sections */
n = 0; addr = 0; n = 0; addr = 0;
@ -329,79 +359,98 @@ redo:
if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR))) if (shf[k] != (s->sh_flags & (SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR)))
continue; continue;
length = s->data_offset; length = s->data_offset;
if (copy) {
if (copy) { /* final step: copy section data to memory */
void *ptr;
if (addr == 0) if (addr == 0)
addr = s->sh_addr; addr = s->sh_addr;
n = (s->sh_addr - addr) + length; n = (s->sh_addr - addr) + length;
ptr = (void*)s->sh_addr; ptr = (void*)s->sh_addr;
if (k == 0) if (k == 0)
ptr = (void*)(s->sh_addr - ptr_diff); ptr = (void*)(s->sh_addr + ptr_diff);
if (NULL == s->data || s->sh_type == SHT_NOBITS) if (NULL == s->data || s->sh_type == SHT_NOBITS)
memset(ptr, 0, length); memset(ptr, 0, length);
else else
memcpy(ptr, s->data, length); memcpy(ptr, s->data, length);
#ifdef _WIN64 #ifdef _WIN64
if (s == s1->uw_pdata) if (s == s1->uw_pdata)
*(void**)mem = win64_add_function_table(s1); s1->run_function_table = win64_add_function_table(s1);
#endif #endif
if (s->data) { free_section(s);
tcc_free(s->data);
s->data = NULL;
s->data_allocated = 0;
}
s->data_offset = 0;
continue; continue;
} }
align = s->sh_addralign - 1; align = s->sh_addralign - 1;
if (++n == 1 && align < (PAGE_ALIGN - 1)) if (++n == 1) {
align = (PAGE_ALIGN - 1); #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
if (max_align < align) /* To avoid that x86 processors would reload cached instructions
max_align = align; each time when data is written in the near, we need to make
addr = k ? mem : mem + ptr_diff; sure that code and data do not share the same 64 byte unit */
if (align < 63)
align = 63;
#endif
/* start new page for different permissions */
if (CONFIG_RUNMEM_RO || k < 2)
align = PAGESIZE - 1;
}
addr = k ? mem + ptr_diff : mem;
offset += -(addr + offset) & align; offset += -(addr + offset) & align;
s->sh_addr = mem ? addr + offset : 0; s->sh_addr = mem ? addr + offset : 0;
offset += length; offset += length;
#if DEBUG_RUNMEN #if DEBUG_RUNMEN
if (mem) if (mem)
printf("%d: %-16s %p len %04x align %04x\n", fprintf(stderr, "%d: %-16s %p len %5x align %04x\n",
k, s->name, (void*)s->sh_addr, length, align + 1); k, s->name, (void*)s->sh_addr, length, align + 1);
#endif #endif
} }
if (copy) { /* set permissions */ if (copy) { /* set permissions */
if (k == 0 && ptr_diff) if (n == 0) /* no data */
continue; /* not with HAVE_SELINUX */ continue;
#ifdef HAVE_SELINUX
if (k == 0) /* SHF_EXECINSTR has its own mapping */
continue;
#endif
f = k; f = k;
#if !CONFIG_RUNMEM_RO if (CONFIG_RUNMEM_RO == 0) {
if (f != 0) if (f != 0)
continue; continue;
f = 3; /* change only SHF_EXECINSTR to rwx */ 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 DEBUG_RUNMEN
fprintf(stderr, "protect %3s %p len %5x\n",
&"rx\0r \0rw\0rwx"[f*3],
(void*)addr, (unsigned)((n + PAGESIZE-1) & ~(PAGESIZE-1)));
#endif
if (protect_pages((void*)addr, n, f) < 0)
return tcc_error_noabort(
"mprotect failed "
"(did you mean to configure --with-selinux?)");
} }
} }
if (copy) if (copy) {
/* remove local symbols and free sections except symtab */
cleanup_symbols(s1);
cleanup_sections(s1);
return 0; return 0;
}
/* relocate symbols */ /* relocate symbols */
relocate_syms(s1, s1->symtab, !(s1->nostdlib)); relocate_syms(s1, s1->symtab, !(s1->nostdlib));
if (s1->nb_errors) if (s1->nb_errors)
return -1; return -1;
if (0 == mem)
return offset + max_align; if (0 == mem) {
offset = (offset + (PAGESIZE-1)) & ~(PAGESIZE-1);
#ifndef HAVE_SELINUX
offset += PAGESIZE; /* extra space to align malloc memory start */
#endif
return offset;
}
#ifdef TCC_TARGET_PE #ifdef TCC_TARGET_PE
s1->pe_imagebase = mem; s1->pe_imagebase = mem;
#endif #else
/* relocate sections */
#ifndef TCC_TARGET_PE
relocate_plt(s1); relocate_plt(s1);
#endif #endif
relocate_sections(s1); relocate_sections(s1);
@ -412,7 +461,7 @@ redo:
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/* allow to run code in memory */ /* allow to run code in memory */
static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long length) static int protect_pages(void *ptr, unsigned long length, int mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
static const unsigned char protect[] = { static const unsigned char protect[] = {
@ -437,7 +486,7 @@ static int set_pages_executable(TCCState *s1, int mode, void *ptr, unsigned long
end = (addr_t)ptr + length; end = (addr_t)ptr + length;
end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
if (mprotect((void *)start, end - start, protect[mode])) if (mprotect((void *)start, end - start, protect[mode]))
return tcc_error_noabort("mprotect failed: did you mean to configure --with-selinux?"); return -1;
/* XXX: BSD sometimes dump core with bad system call */ /* XXX: BSD sometimes dump core with bad system call */
# if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64 # if (defined TCC_TARGET_ARM && !TARGETOS_BSD) || defined TCC_TARGET_ARM64
if (mode == 0 || mode == 3) { if (mode == 0 || mode == 3) {
@ -472,6 +521,7 @@ static void win64_del_function_table(void *p)
} }
} }
#endif #endif
#endif //ndef CONFIG_TCC_BACKTRACE_ONLY #endif //ndef CONFIG_TCC_BACKTRACE_ONLY
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_BACKTRACE #ifdef CONFIG_TCC_BACKTRACE

View file

@ -1,4 +1,3 @@
#include <unistd.h>
#include <libtcc.h> #include <libtcc.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -53,7 +52,7 @@ static int run_callback(const char *src, callback_type callback) {
return -1; return -1;
if (tcc_compile_string(s, src) == -1) if (tcc_compile_string(s, src) == -1)
return -1; return -1;
if (tcc_relocate(s, TCC_RELOCATE_AUTO) == -1) if (tcc_relocate(s) == -1)
return -1; return -1;
ptr = tcc_get_symbol(s, "f"); ptr = tcc_get_symbol(s, "f");

View file

@ -6,8 +6,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include "libtcc.h" #include "libtcc.h"
void handle_error(void *opaque, const char *msg) void handle_error(void *opaque, const char *msg)
@ -59,14 +57,9 @@ int main(int argc, char **argv)
exit(1); exit(1);
} }
assert(tcc_get_error_func(s) == NULL); /* set custom error/warning printer */
assert(tcc_get_error_opaque(s) == NULL);
tcc_set_error_func(s, stderr, handle_error); tcc_set_error_func(s, stderr, handle_error);
assert(tcc_get_error_func(s) == handle_error);
assert(tcc_get_error_opaque(s) == stderr);
/* if tcclib.h and libtcc1.a are not installed, where can we find them */ /* if tcclib.h and libtcc1.a are not installed, where can we find them */
for (i = 1; i < argc; ++i) { for (i = 1; i < argc; ++i) {
char *a = argv[i]; char *a = argv[i];
@ -92,7 +85,7 @@ int main(int argc, char **argv)
tcc_add_symbol(s, "hello", hello); tcc_add_symbol(s, "hello", hello);
/* relocate the code */ /* relocate the code */
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) if (tcc_relocate(s) < 0)
return 1; return 1;
/* get entry symbol */ /* get entry symbol */

View file

@ -128,7 +128,7 @@ void *reloc_state(TCCState *s, const char *entry)
{ {
void *func; void *func;
tcc_add_symbol(s, "add", add); tcc_add_symbol(s, "add", add);
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) { if (tcc_relocate(s) < 0) {
fprintf(stderr, __FILE__ ": could not relocate tcc state.\n"); fprintf(stderr, __FILE__ ": could not relocate tcc state.\n");
return NULL; return NULL;
} }