tinycc/libtcc.c
grischka 5280293d6b make: create native tcc from separate objects
This was already possible using
    make NOTALLINONE=1
and is now the default.

To build as previously from one big source, use
    make ONE_SOURCE=1

Cross compilers are still build from one source because using
separate objects requires separate build directories one per
platform which currently is not (yet) supported by the makefile.

We could probably use gnu-makeish target variables like
    $(I386_CROSS): OUTDIR=build/i386
    $(X64_CROSS): OUTDIR=build/x86-64
and so on ...

Also NEED_FLOAT_TYPES for arm-gen is removed.  It was about
variables that are referenced from outside (libtcc, tccgen).
We could declare them in tcc.h (as with reg_classes) or have
them twice in arm-gen.c.  I chose option 2.
2011-07-14 18:45:37 +02:00

1738 lines
46 KiB
C

/*
* 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
*/
#include "tcc.h"
/********************************************************/
/* global variables */
/* use GNU C extensions */
ST_DATA int gnu_ext = 1;
/* use TinyCC extensions */
ST_DATA int tcc_ext = 1;
/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;
#ifdef CONFIG_TCC_BACKTRACE
ST_DATA int num_callers = 6;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
#endif
/********************************************************/
#ifdef ONE_SOURCE
#include "tccpp.c"
#include "tccgen.c"
#include "tccelf.c"
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
#endif
#ifdef TCC_TARGET_X86_64
#include "x86_64-gen.c"
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#include "i386-asm.c"
#endif
#endif
#ifdef TCC_TARGET_COFF
#include "tcccoff.c"
#endif
#ifdef TCC_TARGET_PE
#include "tccpe.c"
#endif
#endif /* ALL_IN_ONE */
/********************************************************/
#ifndef CONFIG_TCC_ASM
ST_FUNC void asm_instr(void)
{
error("inline asm() not supported");
}
ST_FUNC void asm_global_instr(void)
{
error("inline asm() not supported");
}
#endif
/********************************************************/
#ifdef _WIN32
static char *normalize_slashes(char *path)
{
char *p;
for (p = path; *p; ++p)
if (*p == '\\')
*p = '/';
return path;
}
static HMODULE tcc_module;
/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static void tcc_set_lib_path_w32(TCCState *s)
{
char path[1024], *p;
GetModuleFileNameA(tcc_module, path, sizeof path);
p = tcc_basename(normalize_slashes(strlwr(path)));
if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5))
p -= 5;
else if (p > path)
p--;
*p = 0;
tcc_set_lib_path(s, path);
}
#ifndef CONFIG_TCC_STATIC
void dlclose(void *p)
{
FreeLibrary((HMODULE)p);
}
#endif
#ifdef LIBTCC_AS_DLL
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
if (DLL_PROCESS_ATTACH == dwReason)
tcc_module = hDll;
return TRUE;
}
#endif
#endif
/********************************************************/
/* copy a string and truncate it. */
PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
{
char *q, *q_end;
int c;
if (buf_size > 0) {
q = buf;
q_end = buf + buf_size - 1;
while (q < q_end) {
c = *s++;
if (c == '\0')
break;
*q++ = c;
}
*q = '\0';
}
return buf;
}
/* strcat and truncate. */
PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
/* extract the basename of a file */
PUB_FUNC char *tcc_basename(const char *name)
{
char *p = strchr(name, 0);
while (p > name && !IS_PATHSEP(p[-1]))
--p;
return p;
}
/* extract extension part of a file
*
* (if no extension, return pointer to end-of-string)
*/
PUB_FUNC char *tcc_fileextension (const char *name)
{
char *b = tcc_basename(name);
char *e = strrchr(b, '.');
return e ? e : strchr(b, 0);
}
/********************************************************/
/* memory management */
#undef free
#undef malloc
#undef realloc
#ifdef MEM_DEBUG
int mem_cur_size;
int mem_max_size;
unsigned malloc_usable_size(void*);
#endif
PUB_FUNC void tcc_free(void *ptr)
{
#ifdef MEM_DEBUG
mem_cur_size -= malloc_usable_size(ptr);
#endif
free(ptr);
}
PUB_FUNC void *tcc_malloc(unsigned long size)
{
void *ptr;
ptr = malloc(size);
if (!ptr && size)
error("memory full");
#ifdef MEM_DEBUG
mem_cur_size += malloc_usable_size(ptr);
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
#endif
return ptr;
}
PUB_FUNC void *tcc_mallocz(unsigned long size)
{
void *ptr;
ptr = tcc_malloc(size);
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
void *ptr1;
#ifdef MEM_DEBUG
mem_cur_size -= malloc_usable_size(ptr);
#endif
ptr1 = realloc(ptr, size);
#ifdef MEM_DEBUG
/* NOTE: count not correct if alloc error, but not critical */
mem_cur_size += malloc_usable_size(ptr1);
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
#endif
return ptr1;
}
PUB_FUNC char *tcc_strdup(const char *str)
{
char *ptr;
ptr = tcc_malloc(strlen(str) + 1);
strcpy(ptr, str);
return ptr;
}
PUB_FUNC 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)
/********************************************************/
/* dynarrays */
PUB_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data)
{
int nb, nb_alloc;
void **pp;
nb = *nb_ptr;
pp = *ptab;
/* every power of two we double array size */
if ((nb & (nb - 1)) == 0) {
if (!nb)
nb_alloc = 1;
else
nb_alloc = nb * 2;
pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
if (!pp)
error("memory full");
*ptab = pp;
}
pp[nb++] = data;
*nb_ptr = nb;
}
PUB_FUNC void dynarray_reset(void *pp, int *n)
{
void **p;
for (p = *(void***)pp; *n; ++p, --*n)
if (*p)
tcc_free(*p);
tcc_free(*(void**)pp);
*(void**)pp = NULL;
}
/* out must not point to a valid dynarray since a new one is created */
PUB_FUNC int tcc_split_path(const char *in, const char * const *prefixs,
int nb_prefixs, char ***out)
{
int i, nb_comps = 0;
char *path;
const char *end;
size_t size;
*out = NULL;
do {
end = in;
while (*end && *end != ':')
++end;
for (i = 0; i < nb_prefixs; i++) {
size = (strlen(prefixs[i]) + 1) * sizeof(char)
+ (end - in);
path = tcc_malloc(size);
pstrcpy(path, size, prefixs[i]);
pstrcat(path, size, in);
dynarray_add((void ***) out, &nb_comps, path);
}
in = end + 1;
} while (*end);
return nb_comps;
}
/* we use our own 'finite' function to avoid potential problems with
non standard math libs */
/* XXX: endianness dependent */
ST_FUNC int ieee_finite(double d)
{
int *p = (int *)&d;
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
}
/********************************************************/
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
Section *sec;
sec = tcc_mallocz(sizeof(Section) + strlen(name));
strcpy(sec->name, name);
sec->sh_type = sh_type;
sec->sh_flags = sh_flags;
switch(sh_type) {
case SHT_HASH:
case SHT_REL:
case SHT_RELA:
case SHT_DYNSYM:
case SHT_SYMTAB:
case SHT_DYNAMIC:
sec->sh_addralign = 4;
break;
case SHT_STRTAB:
sec->sh_addralign = 1;
break;
default:
sec->sh_addralign = 32; /* default conservative alignment */
break;
}
if (sh_flags & SHF_PRIVATE) {
dynarray_add((void ***)&s1->priv_sections, &s1->nb_priv_sections, sec);
} else {
sec->sh_num = s1->nb_sections;
dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
}
return sec;
}
static void free_section(Section *s)
{
tcc_free(s->data);
}
/* realloc section and set its content to zero */
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
{
unsigned long size;
unsigned char *data;
size = sec->data_allocated;
if (size == 0)
size = 1;
while (size < new_size)
size = size * 2;
data = tcc_realloc(sec->data, size);
if (!data)
error("memory full");
memset(data + sec->data_allocated, 0, size - sec->data_allocated);
sec->data = data;
sec->data_allocated = size;
}
/* reserve at least 'size' bytes in section 'sec' from
sec->data_offset. */
ST_FUNC void *section_ptr_add(Section *sec, unsigned long size)
{
unsigned long offset, offset1;
offset = sec->data_offset;
offset1 = offset + size;
if (offset1 > sec->data_allocated)
section_realloc(sec, offset1);
sec->data_offset = offset1;
return sec->data + offset;
}
/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
{
if (size > sec->data_allocated)
section_realloc(sec, size);
if (size > sec->data_offset)
sec->data_offset = size;
}
/* return a reference to a section, and create it if it does not
exists */
ST_FUNC Section *find_section(TCCState *s1, const char *name)
{
Section *sec;
int i;
for(i = 1; i < s1->nb_sections; i++) {
sec = s1->sections[i];
if (!strcmp(name, sec->name))
return sec;
}
/* sections are created as PROGBITS */
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
}
/* update sym->c so that it points to an external symbol in section
'section' with value 'value' */
ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
unsigned long value, unsigned long size,
int can_add_underscore)
{
int sym_type, sym_bind, sh_num, info, other;
ElfW(Sym) *esym;
const char *name;
char buf1[256];
if (section == NULL)
sh_num = SHN_UNDEF;
else if (section == SECTION_ABS)
sh_num = SHN_ABS;
else
sh_num = section->sh_num;
if ((sym->type.t & VT_BTYPE) == VT_FUNC) {
sym_type = STT_FUNC;
} else if ((sym->type.t & VT_BTYPE) == VT_VOID) {
sym_type = STT_NOTYPE;
} else {
sym_type = STT_OBJECT;
}
if (sym->type.t & VT_STATIC)
sym_bind = STB_LOCAL;
else {
if (sym->type.t & VT_WEAK)
sym_bind = STB_WEAK;
else
sym_bind = STB_GLOBAL;
}
if (!sym->c) {
name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check) {
char buf[32];
/* XXX: avoid doing that for statics ? */
/* if bound checking is activated, we change some function
names by adding the "__bound" prefix */
switch(sym->v) {
#ifdef TCC_TARGET_PE
/* XXX: we rely only on malloc hooks */
case TOK_malloc:
case TOK_free:
case TOK_realloc:
case TOK_memalign:
case TOK_calloc:
#endif
case TOK_memcpy:
case TOK_memmove:
case TOK_memset:
case TOK_strlen:
case TOK_strcpy:
case TOK_alloca:
strcpy(buf, "__bound_");
strcat(buf, name);
name = buf;
break;
}
}
#endif
other = 0;
#ifdef TCC_TARGET_PE
if (sym->type.t & VT_EXPORT)
other |= 1;
if (sym_type == STT_FUNC && sym->type.ref) {
int attr = sym->type.ref->r;
if (FUNC_EXPORT(attr))
other |= 1;
if (FUNC_CALL(attr) == FUNC_STDCALL && can_add_underscore) {
sprintf(buf1, "_%s@%d", name, FUNC_ARGS(attr) * PTR_SIZE);
name = buf1;
other |= 2;
can_add_underscore = 0;
}
} else {
if (find_elf_sym(tcc_state->dynsymtab_section, name))
other |= 4;
if (sym->type.t & VT_IMPORT)
other |= 4;
}
#endif
if (tcc_state->leading_underscore && can_add_underscore) {
buf1[0] = '_';
pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
name = buf1;
}
if (sym->asm_label) {
name = sym->asm_label;
}
info = ELFW(ST_INFO)(sym_bind, sym_type);
sym->c = add_elf_sym(symtab_section, value, size, info, other, sh_num, name);
} else {
esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
esym->st_value = value;
esym->st_size = size;
esym->st_shndx = sh_num;
}
}
ST_FUNC void put_extern_sym(Sym *sym, Section *section,
unsigned long value, unsigned long size)
{
put_extern_sym2(sym, section, value, size, 1);
}
/* add a new relocation entry to symbol 'sym' in section 's' */
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
{
int c = 0;
if (sym) {
if (0 == sym->c)
put_extern_sym(sym, NULL, 0, 0);
c = sym->c;
}
/* now we can add ELF relocation info */
put_elf_reloc(symtab_section, s, offset, type, c);
}
/********************************************************/
static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
{
int len;
len = strlen(buf);
vsnprintf(buf + len, buf_size - len, fmt, ap);
}
static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strcat_vprintf(buf, buf_size, fmt, ap);
va_end(ap);
}
static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
{
char buf[2048];
BufferedFile **f;
buf[0] = '\0';
if (file) {
for(f = s1->include_stack; f < s1->include_stack_ptr; f++)
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
(*f)->filename, (*f)->line_num);
if (file->line_num > 0) {
strcat_printf(buf, sizeof(buf),
"%s:%d: ", file->filename, file->line_num);
} else {
strcat_printf(buf, sizeof(buf),
"%s: ", file->filename);
}
} else {
strcat_printf(buf, sizeof(buf),
"tcc: ");
}
if (is_warning)
strcat_printf(buf, sizeof(buf), "warning: ");
else
strcat_printf(buf, sizeof(buf), "error: ");
strcat_vprintf(buf, sizeof(buf), fmt, ap);
if (!s1->error_func) {
/* default case: stderr */
fprintf(stderr, "%s\n", buf);
} else {
s1->error_func(s1->error_opaque, buf);
}
if (!is_warning || s1->warn_error)
s1->nb_errors++;
}
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
void (*error_func)(void *opaque, const char *msg))
{
s->error_opaque = error_opaque;
s->error_func = error_func;
}
/* error without aborting current compilation */
PUB_FUNC void error_noabort(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
}
PUB_FUNC void error(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
/* better than nothing: in some cases, we accept to handle errors */
if (s1->error_set_jmp_enabled) {
longjmp(s1->error_jmp_buf, 1);
} else {
/* XXX: eliminate this someday */
exit(1);
}
}
PUB_FUNC void expect(const char *msg)
{
error("%s expected", msg);
}
PUB_FUNC void warning(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
if (s1->warn_none)
return;
va_start(ap, fmt);
error1(s1, 1, fmt, ap);
va_end(ap);
}
/********************************************************/
/* I/O layer */
ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
BufferedFile *bf;
int buflen = initlen ? initlen : IO_BUF_SIZE;
bf = tcc_malloc(sizeof(BufferedFile) + buflen);
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer + initlen;
bf->buf_end[0] = CH_EOB; /* put eob symbol */
pstrcpy(bf->filename, sizeof(bf->filename), filename);
#ifdef _WIN32
normalize_slashes(bf->filename);
#endif
bf->line_num = 1;
bf->ifndef_macro = 0;
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
bf->fd = -1;
bf->prev = file;
file = bf;
}
ST_FUNC void tcc_close(void)
{
BufferedFile *bf = file;
if (bf->fd > 0) {
close(bf->fd);
total_lines += bf->line_num;
}
file = bf->prev;
tcc_free(bf);
}
ST_FUNC int tcc_open(TCCState *s1, const char *filename)
{
int fd;
if (strcmp(filename, "-") == 0)
fd = 0, filename = "stdin";
else
fd = open(filename, O_RDONLY | O_BINARY);
if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
printf("%s %*s%s\n", fd < 0 ? "nf":"->",
(int)(s1->include_stack_ptr - s1->include_stack), "", filename);
if (fd < 0)
return -1;
tcc_open_bf(s1, filename, 0);
file->fd = fd;
return fd;
}
/* compile the C file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1)
{
Sym *define_start;
SValue *pvtop;
char buf[512];
volatile int section_sym;
#ifdef INC_DEBUG
printf("%s: **** new file\n", file->filename);
#endif
preprocess_init(s1);
cur_text_section = NULL;
funcname = "";
anon_sym = SYM_FIRST_ANOM;
/* file info: full path + filename */
section_sym = 0; /* avoid warning */
if (s1->do_debug) {
section_sym = put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
text_section->sh_num, NULL);
getcwd(buf, sizeof(buf));
#ifdef _WIN32
normalize_slashes(buf);
#endif
pstrcat(buf, sizeof(buf), "/");
put_stabs_r(buf, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
put_stabs_r(file->filename, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
}
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL
symbols can be safely used */
put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
SHN_ABS, file->filename);
/* define some often used types */
int_type.t = VT_INT;
char_pointer_type.t = VT_BYTE;
mk_pointer(&char_pointer_type);
func_old_type.t = VT_FUNC;
func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
float_type.t = VT_FLOAT;
double_type.t = VT_DOUBLE;
func_float_type.t = VT_FUNC;
func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD);
func_double_type.t = VT_FUNC;
func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD);
#endif
#if 0
/* define 'void *alloca(unsigned int)' builtin function */
{
Sym *s1;
p = anon_sym++;
sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW);
s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
s1->next = NULL;
sym->next = s1;
sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
}
#endif
define_start = define_stack;
nocode_wanted = 1;
if (setjmp(s1->error_jmp_buf) == 0) {
s1->nb_errors = 0;
s1->error_set_jmp_enabled = 1;
ch = file->buf_ptr[0];
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM;
pvtop = vtop;
next();
decl(VT_CONST);
if (tok != TOK_EOF)
expect("declaration");
if (pvtop != vtop)
warning("internal compiler error: vstack leak? (%d)", vtop - pvtop);
/* end of translation unit info */
if (s1->do_debug) {
put_stabs_r(NULL, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
}
}
s1->error_set_jmp_enabled = 0;
/* reset define stack, but leave -Dsymbols (may be incorrect if
they are undefined) */
free_defines(define_start);
gen_inline_functions();
sym_pop(&global_stack, NULL);
sym_pop(&local_stack, NULL);
return s1->nb_errors != 0 ? -1 : 0;
}
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
int len, ret;
len = strlen(str);
tcc_open_bf(s, "<string>", len);
memcpy(file->buffer, str, len);
ret = tcc_compile(s);
tcc_close();
return ret;
}
/* define a preprocessor symbol. A value can also be provided with the '=' operator */
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
int len1, len2;
/* default value */
if (!value)
value = "1";
len1 = strlen(sym);
len2 = strlen(value);
/* init file structure */
tcc_open_bf(s1, "<define>", len1 + len2 + 1);
memcpy(file->buffer, sym, len1);
file->buffer[len1] = ' ';
memcpy(file->buffer + len1 + 1, value, len2);
/* parse with define parser */
ch = file->buf_ptr[0];
next_nomacro();
parse_define();
tcc_close();
}
/* undefine a preprocessor symbol */
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
TokenSym *ts;
Sym *s;
ts = tok_alloc(sym, strlen(sym));
s = define_find(ts->tok);
/* undefine symbol by putting an invalid name */
if (s)
define_undef(s);
}
static void tcc_cleanup(void)
{
int i, n;
if (NULL == tcc_state)
return;
tcc_state = NULL;
/* free -D defines */
free_defines(NULL);
/* free tokens */
n = tok_ident - TOK_IDENT;
for(i = 0; i < n; i++)
tcc_free(table_ident[i]);
tcc_free(table_ident);
/* free sym_pools */
dynarray_reset(&sym_pools, &nb_sym_pools);
/* string buffer */
cstr_free(&tokcstr);
/* reset symbol stack */
sym_free_first = NULL;
/* cleanup from error/setjmp */
macro_ptr = NULL;
}
LIBTCCAPI TCCState *tcc_new(void)
{
TCCState *s;
char buffer[100];
int a,b,c;
tcc_cleanup();
s = tcc_mallocz(sizeof(TCCState));
if (!s)
return NULL;
tcc_state = s;
#ifdef _WIN32
tcc_set_lib_path_w32(s);
#else
tcc_set_lib_path(s, CONFIG_TCCDIR);
#endif
s->output_type = TCC_OUTPUT_MEMORY;
preprocess_new();
s->include_stack_ptr = s->include_stack;
/* we add dummy defines for some special macros to speed up tests
and to have working defined() */
define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
/* standard defines */
tcc_define_symbol(s, "__STDC__", NULL);
tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
#if defined(TCC_TARGET_I386)
tcc_define_symbol(s, "__i386__", NULL);
tcc_define_symbol(s, "__i386", NULL);
tcc_define_symbol(s, "i386", NULL);
#endif
#if defined(TCC_TARGET_X86_64)
tcc_define_symbol(s, "__x86_64__", NULL);
#endif
#if defined(TCC_TARGET_ARM)
tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
tcc_define_symbol(s, "__arm_elf__", NULL);
tcc_define_symbol(s, "__arm_elf", NULL);
tcc_define_symbol(s, "arm_elf", NULL);
tcc_define_symbol(s, "__arm__", NULL);
tcc_define_symbol(s, "__arm", NULL);
tcc_define_symbol(s, "arm", NULL);
tcc_define_symbol(s, "__APCS_32__", NULL);
#endif
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "_WIN32", NULL);
#ifdef TCC_TARGET_X86_64
tcc_define_symbol(s, "_WIN64", NULL);
#endif
#else
tcc_define_symbol(s, "__unix__", NULL);
tcc_define_symbol(s, "__unix", NULL);
tcc_define_symbol(s, "unix", NULL);
#if defined(__FreeBSD__)
#define str(s) #s
tcc_define_symbol(s, "__FreeBSD__", str( __FreeBSD__));
#undef str
#endif
#if defined(__FreeBSD_kernel__)
tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
#endif
#if defined(__linux)
tcc_define_symbol(s, "__linux__", NULL);
tcc_define_symbol(s, "__linux", NULL);
#endif
#endif
/* tiny C specific defines */
sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
sprintf(buffer, "%d", a*10000 + b*100 + c);
tcc_define_symbol(s, "__TINYC__", buffer);
/* tiny C & gcc defines */
#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
#else
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
#endif
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
#else
tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
#endif
/* glibc defines */
tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)", "name proto __asm__ (#alias) __THROW");
#ifndef TCC_TARGET_PE
/* default library paths */
tcc_add_library_path(s, CONFIG_TCC_CRT_PREFIX);
tcc_add_library_path(s, CONFIG_SYSROOT CONFIG_TCC_LDDIR);
tcc_add_library_path(s, CONFIG_SYSROOT "/usr/local"CONFIG_TCC_LDDIR);
#ifdef CONFIG_TCC_EXTRA_LDDIR
{
int i, nb_extra_lddirs, nb_prefixs;
char **extra_lddirs;
char extra_lddir_str[] = CONFIG_TCC_EXTRA_LDDIR;
const char lddir_prefix1[] = CONFIG_SYSROOT;
const char lddir_prefix2[] = CONFIG_SYSROOT "/usr/local";
const char * const lddir_prefixs[] = {lddir_prefix1, lddir_prefix2};
nb_prefixs = sizeof lddir_prefixs / sizeof *lddir_prefixs;
nb_extra_lddirs = tcc_split_path(CONFIG_TCC_EXTRA_LDDIR,
lddir_prefixs, nb_prefixs,
&extra_lddirs);
for (i = 0; i < nb_extra_lddirs; i++)
tcc_add_library_path(s, extra_lddirs[i]);
dynarray_reset(&extra_lddirs, &nb_extra_lddirs);
}
#endif
#endif
/* no section zero */
dynarray_add((void ***)&s->sections, &s->nb_sections, NULL);
/* create standard sections */
text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
/* symbols are always generated for linking stage */
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
".strtab",
".hashtab", SHF_PRIVATE);
strtab_section = symtab_section->link;
/* private symbol table for dynamic symbols */
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
".dynstrtab",
".dynhashtab", SHF_PRIVATE);
s->alacarte_link = 1;
s->nocommon = 1;
#ifdef CHAR_IS_UNSIGNED
s->char_is_unsigned = 1;
#endif
/* enable this if you want symbols with leading underscore on windows: */
#if defined(TCC_TARGET_PE) && 0
s->leading_underscore = 1;
#endif
if (s->section_align == 0)
s->section_align = ELF_PAGE_SIZE;
#ifdef TCC_TARGET_I386
s->seg_size = 32;
#endif
return s;
}
LIBTCCAPI void tcc_delete(TCCState *s1)
{
int i;
tcc_cleanup();
/* free all sections */
for(i = 1; i < s1->nb_sections; i++)
free_section(s1->sections[i]);
dynarray_reset(&s1->sections, &s1->nb_sections);
for(i = 0; i < s1->nb_priv_sections; i++)
free_section(s1->priv_sections[i]);
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
/* free any loaded DLLs */
for ( i = 0; i < s1->nb_loaded_dlls; i++) {
DLLReference *ref = s1->loaded_dlls[i];
if ( ref->handle )
dlclose(ref->handle);
}
/* free loaded dlls array */
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
/* free library paths */
dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
/* free include paths */
dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes);
dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
tcc_free(s1->tcc_lib_path);
dynarray_reset(&s1->input_files, &s1->nb_input_files);
dynarray_reset(&s1->input_libs, &s1->nb_input_libs);
dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
#ifdef HAVE_SELINUX
munmap (s1->write_mem, s1->mem_size);
munmap (s1->runtime_mem, s1->mem_size);
#else
tcc_free(s1->runtime_mem);
#endif
tcc_free(s1);
}
LIBTCCAPI int tcc_add_include_path(TCCState *s1, const char *pathname)
{
char *pathname1;
pathname1 = tcc_strdup(pathname);
dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1);
return 0;
}
LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s1, const char *pathname)
{
char *pathname1;
pathname1 = tcc_strdup(pathname);
dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1);
return 0;
}
ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
const char *ext;
ElfW(Ehdr) ehdr;
int fd, ret, size;
/* find source file type with extension */
ext = tcc_fileextension(filename);
if (ext[0])
ext++;
#ifdef CONFIG_TCC_ASM
/* if .S file, define __ASSEMBLER__ like gcc does */
if (!strcmp(ext, "S"))
tcc_define_symbol(s1, "__ASSEMBLER__", NULL);
#endif
/* open the file */
ret = tcc_open(s1, filename);
if (ret < 0) {
if (flags & AFF_PRINT_ERROR)
error_noabort("file '%s' not found", filename);
return ret;
}
/* update target deps */
dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps,
tcc_strdup(filename));
if (flags & AFF_PREPROCESS) {
ret = tcc_preprocess(s1);
goto the_end;
}
if (!ext[0] || !PATHCMP(ext, "c")) {
/* C file assumed */
ret = tcc_compile(s1);
goto the_end;
}
#ifdef CONFIG_TCC_ASM
if (!strcmp(ext, "S")) {
/* preprocessed assembler */
ret = tcc_assemble(s1, 1);
goto the_end;
}
if (!strcmp(ext, "s")) {
/* non preprocessed assembler */
ret = tcc_assemble(s1, 0);
goto the_end;
}
#endif
fd = file->fd;
/* assume executable format: auto guess file type */
size = read(fd, &ehdr, sizeof(ehdr));
lseek(fd, 0, SEEK_SET);
if (size <= 0) {
error_noabort("could not read header");
goto the_end;
}
if (size == sizeof(ehdr) &&
ehdr.e_ident[0] == ELFMAG0 &&
ehdr.e_ident[1] == ELFMAG1 &&
ehdr.e_ident[2] == ELFMAG2 &&
ehdr.e_ident[3] == ELFMAG3) {
/* do not display line number if error */
file->line_num = 0;
if (ehdr.e_type == ET_REL) {
ret = tcc_load_object_file(s1, fd, 0);
goto the_end;
}
#ifndef TCC_TARGET_PE
if (ehdr.e_type == ET_DYN) {
if (s1->output_type == TCC_OUTPUT_MEMORY) {
void *h;
h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY);
if (h)
ret = 0;
} else {
ret = tcc_load_dll(s1, fd, filename,
(flags & AFF_REFERENCED_DLL) != 0);
}
goto the_end;
}
#endif
error_noabort("unrecognized ELF file");
goto the_end;
}
if (memcmp((char *)&ehdr, ARMAG, 8) == 0) {
file->line_num = 0; /* do not display line number if error */
ret = tcc_load_archive(s1, fd);
goto the_end;
}
#ifdef TCC_TARGET_COFF
if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) {
ret = tcc_load_coff(s1, fd);
goto the_end;
}
#endif
#ifdef TCC_TARGET_PE
ret = pe_load_file(s1, filename, fd);
#else
/* as GNU ld, consider it is an ld script if not recognized */
ret = tcc_load_ldscript(s1);
#endif
if (ret < 0)
error_noabort("unrecognized file type");
the_end:
tcc_close();
return ret;
}
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
{
dynarray_add((void ***)&s->input_files, &s->nb_input_files, tcc_strdup(filename));
if (s->output_type == TCC_OUTPUT_PREPROCESS)
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS);
else
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR);
}
LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
{
char *pathname1;
pathname1 = tcc_strdup(pathname);
dynarray_add((void ***)&s->library_paths, &s->nb_library_paths, pathname1);
return 0;
}
/* find and load a dll. Return non zero if not found */
/* XXX: add '-rpath' option support ? */
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
{
char buf[1024];
int i;
for(i = 0; i < s->nb_library_paths; i++) {
snprintf(buf, sizeof(buf), "%s/%s",
s->library_paths[i], filename);
if (tcc_add_file_internal(s, buf, flags) == 0)
return 0;
}
return -1;
}
/* the library name is the same as the argument of the '-l' option */
LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
{
char buf[1024];
int i;
dynarray_add((void ***)&s->input_libs, &s->nb_input_libs, tcc_strdup(libraryname));
/* first we look for the dynamic library if not static linking */
if (!s->static_link) {
#ifdef TCC_TARGET_PE
if (pe_add_dll(s, libraryname) == 0)
return 0;
#else
snprintf(buf, sizeof(buf), "lib%s.so", libraryname);
if (tcc_add_dll(s, buf, 0) == 0)
return 0;
#endif
}
/* then we look for the static library */
for(i = 0; i < s->nb_library_paths; i++) {
snprintf(buf, sizeof(buf), "%s/lib%s.a",
s->library_paths[i], libraryname);
if (tcc_add_file_internal(s, buf, 0) == 0)
return 0;
}
return -1;
}
LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
{
#ifdef TCC_TARGET_PE
pe_putimport(s, 0, name, val);
#else
add_elf_sym(symtab_section, (uplong)val, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
SHN_ABS, name);
#endif
return 0;
}
LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
{
char buf[1024];
s->output_type = output_type;
if (!s->nostdinc) {
/* default include paths */
/* -isystem paths have already been handled */
#ifndef TCC_TARGET_PE
{
int i, nb_extra_incdirs, nb_prefixs;
char **extra_incdirs;
const char incdir_prefix1[] = CONFIG_SYSROOT "/usr/local/include";
const char incdir_prefix2[] = CONFIG_SYSROOT "/usr/include";
const char * const incdir_prefixs[] = {incdir_prefix1,
incdir_prefix2};
nb_prefixs = sizeof incdir_prefixs / sizeof *incdir_prefixs;
nb_extra_incdirs = tcc_split_path(CONFIG_TCC_INCSUBDIR,
incdir_prefixs, nb_prefixs,
&extra_incdirs);
for (i = 0; i < nb_extra_incdirs; i++)
tcc_add_sysinclude_path(s, extra_incdirs[i]);
dynarray_reset(&extra_incdirs, &nb_extra_incdirs);
}
#endif
snprintf(buf, sizeof(buf), "%s/include", s->tcc_lib_path);
tcc_add_sysinclude_path(s, buf);
#ifdef TCC_TARGET_PE
snprintf(buf, sizeof(buf), "%s/include/winapi", s->tcc_lib_path);
tcc_add_sysinclude_path(s, buf);
#endif
}
/* if bound checking, then add corresponding sections */
#ifdef CONFIG_TCC_BCHECK
if (s->do_bounds_check) {
/* define symbol */
tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
/* create bounds sections */
bounds_section = new_section(s, ".bounds",
SHT_PROGBITS, SHF_ALLOC);
lbounds_section = new_section(s, ".lbounds",
SHT_PROGBITS, SHF_ALLOC);
}
#endif
if (s->char_is_unsigned) {
tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
}
/* add debug sections */
if (s->do_debug) {
/* stab symbols */
stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
stab_section->sh_entsize = sizeof(Stab_Sym);
stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
put_elf_str(stabstr_section, "");
stab_section->link = stabstr_section;
/* put first entry */
put_stabs("", 0, 0, 0, 0);
}
/* add libc crt1/crti objects */
#ifndef TCC_TARGET_PE
if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
!s->nostdlib) {
if (output_type != TCC_OUTPUT_DLL)
tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crt1.o");
tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crti.o");
}
#endif
#ifdef TCC_TARGET_PE
#ifdef CONFIG_TCC_CROSSLIB
snprintf(buf, sizeof(buf), "%s/" CONFIG_TCC_CROSSLIB, s->tcc_lib_path);
tcc_add_library_path(s, buf);
#endif
snprintf(buf, sizeof(buf), "%s/lib", s->tcc_lib_path);
tcc_add_library_path(s, buf);
#ifdef _WIN32
if (GetSystemDirectory(buf, sizeof buf))
tcc_add_library_path(s, buf);
#endif
#endif
return 0;
}
#define WD_ALL 0x0001 /* warning is activated when using -Wall */
#define FD_INVERT 0x0002 /* invert value before storing */
typedef struct FlagDef {
uint16_t offset;
uint16_t flags;
const char *name;
} FlagDef;
static const FlagDef warning_defs[] = {
{ offsetof(TCCState, warn_unsupported), 0, "unsupported" },
{ offsetof(TCCState, warn_write_strings), 0, "write-strings" },
{ offsetof(TCCState, warn_error), 0, "error" },
{ offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
"implicit-function-declaration" },
};
ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
const char *name, int value)
{
int i;
const FlagDef *p;
const char *r;
r = name;
if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') {
r += 3;
value = !value;
}
for(i = 0, p = flags; i < nb_flags; i++, p++) {
if (!strcmp(r, p->name))
goto found;
}
return -1;
found:
if (p->flags & FD_INVERT)
value = !value;
*(int *)((uint8_t *)s + p->offset) = value;
return 0;
}
/* set/reset a warning */
LIBTCCAPI int tcc_set_warning(TCCState *s, const char *warning_name, int value)
{
int i;
const FlagDef *p;
if (!strcmp(warning_name, "all")) {
for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) {
if (p->flags & WD_ALL)
*(int *)((uint8_t *)s + p->offset) = 1;
}
return 0;
} else {
return set_flag(s, warning_defs, countof(warning_defs),
warning_name, value);
}
}
static const FlagDef flag_defs[] = {
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
};
/* set/reset a flag */
PUB_FUNC int tcc_set_flag(TCCState *s, const char *flag_name, int value)
{
return set_flag(s, flag_defs, countof(flag_defs),
flag_name, value);
}
static int strstart(const char *str, const char *val, char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = (char *) p;
return 1;
}
/* Like strstart, but automatically takes into account that ld options can
*
* - start with double or single dash (e.g. '--soname' or '-soname')
* - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
* or '-Wl,-soname=x.so')
*
* you provide `val` always in 'option[=]' form (no leading -)
*/
static int link_option(const char *str, const char *val, char **ptr)
{
const char *p, *q;
/* there should be 1 or 2 dashes */
if (*str++ != '-')
return 0;
if (*str == '-')
str++;
/* then str & val should match (potentialy up to '=') */
p = str;
q = val;
while (*q != '\0' && *q != '=') {
if (*p != *q)
return 0;
p++;
q++;
}
/* '=' near eos means ',' or '=' is ok */
if (*q == '=') {
if (*p != ',' && *p != '=')
return 0;
p++;
q++;
}
if (ptr)
*ptr = (char *) p;
return 1;
}
/* set linker options */
PUB_FUNC const char * tcc_set_linker(TCCState *s, char *option, int multi)
{
char *p = option;
char *end;
while (option && *option) {
end = NULL;
if (link_option(option, "Bsymbolic", &p)) {
s->symbolic = TRUE;
} else if (link_option(option, "fini=", &p)) {
s->fini_symbol = p;
if (s->warn_unsupported)
warning("ignoring -fini %s", p);
} else if (link_option(option, "image-base=", &p)) {
s->text_addr = strtoul(p, &end, 16);
s->has_text_addr = 1;
} else if (link_option(option, "init=", &p)) {
s->init_symbol = p;
if (s->warn_unsupported)
warning("ignoring -init %s", p);
} else if (link_option(option, "oformat=", &p)) {
#if defined(TCC_TARGET_PE)
if (strstart(p, "pe-", NULL)) {
#else
#if defined(TCC_TARGET_X86_64)
if (strstart(p, "elf64-", NULL)) {
#else
if (strstart(p, "elf32-", NULL)) {
#endif
#endif
s->output_format = TCC_OUTPUT_FORMAT_ELF;
} else if (!strcmp(p, "binary")) {
s->output_format = TCC_OUTPUT_FORMAT_BINARY;
} else
#ifdef TCC_TARGET_COFF
if (!strcmp(p, "coff")) {
s->output_format = TCC_OUTPUT_FORMAT_COFF;
} else
#endif
{
return p;
}
} else if (link_option(option, "rpath=", &p)) {
s->rpath = p;
} else if (link_option(option, "section-alignment=", &p)) {
s->section_align = strtoul(p, &end, 16);
} else if (link_option(option, "soname=", &p)) {
s->soname = p;
multi = 0;
#ifdef TCC_TARGET_PE
} else if (link_option(option, "file-alignment=", &p)) {
s->pe_file_align = strtoul(p, &end, 16);
} else if (link_option(option, "stack=", &p)) {
s->pe_stack_size = strtoul(p, &end, 10);
} else if (link_option(option, "subsystem=", &p)) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
if (!strcmp(p, "native")) {
s->pe_subsystem = 1;
} else if (!strcmp(p, "console")) {
s->pe_subsystem = 3;
} else if (!strcmp(p, "gui")) {
s->pe_subsystem = 2;
} else if (!strcmp(p, "posix")) {
s->pe_subsystem = 7;
} else if (!strcmp(p, "efiapp")) {
s->pe_subsystem = 10;
} else if (!strcmp(p, "efiboot")) {
s->pe_subsystem = 11;
} else if (!strcmp(p, "efiruntime")) {
s->pe_subsystem = 12;
} else if (!strcmp(p, "efirom")) {
s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
if (!strcmp(p, "wince")) {
s->pe_subsystem = 9;
#endif
} else {
return p;
}
#endif
} else if (link_option(option, "Ttext=", &p)) {
s->text_addr = strtoul(p, &end, 16);
s->has_text_addr = 1;
} else {
return option;
}
if (multi) {
option = NULL;
p = strchr( (end) ? end : p, ',');
if (p) {
*p = 0; /* terminate last option */
option = ++p;
}
} else
option = NULL;
}
return NULL;
}
PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time)
{
double tt;
tt = (double)total_time / 1000000.0;
if (tt < 0.001)
tt = 0.001;
if (total_bytes < 1)
total_bytes = 1;
printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n",
tok_ident - TOK_IDENT, total_lines, total_bytes,
tt, (int)(total_lines / tt),
total_bytes / tt / 1000000.0);
}
/* set CONFIG_TCCDIR at runtime */
LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
{
tcc_free(s->tcc_lib_path);
s->tcc_lib_path = tcc_strdup(path);
}
PUB_FUNC void set_num_callers(int n)
{
#ifdef CONFIG_TCC_BACKTRACE
num_callers = n;
#endif
}
LIBTCCAPI const char *tcc_default_target(TCCState *s)
{
/* FIXME will break in multithreaded case */
static char outfile_default[1024];
char *ext;
const char *name =
strcmp(s->input_files[0], "-") == 0 ? "a"
: tcc_basename(s->input_files[0]);
pstrcpy(outfile_default, sizeof(outfile_default), name);
ext = tcc_fileextension(outfile_default);
#ifdef TCC_TARGET_PE
if (s->output_type == TCC_OUTPUT_DLL)
strcpy(ext, ".dll");
else
if (s->output_type == TCC_OUTPUT_EXE)
strcpy(ext, ".exe");
else
#endif
if (( (s->output_type == TCC_OUTPUT_OBJ && !s->reloc_output) ||
(s->output_type == TCC_OUTPUT_PREPROCESS) )
&& *ext)
strcpy(ext, ".o");
else
pstrcpy(outfile_default, sizeof(outfile_default), "a.out");
return outfile_default;
}
LIBTCCAPI void tcc_gen_makedeps(TCCState *s, const char *target, const char *filename)
{
FILE *depout;
char buf[1024], *ext;
int i;
if (!target)
target = tcc_default_target(s);
if (!filename) {
/* compute filename automatically
* dir/file.o -> dir/file.d */
pstrcpy(buf, sizeof(buf), target);
ext = tcc_fileextension(buf);
pstrcpy(ext, sizeof(buf) - (ext-buf), ".d");
filename = buf;
}
if (s->verbose)
printf("<- %s\n", filename);
/* XXX return err codes instead of error() ? */
depout = fopen(filename, "w");
if (!depout)
error("could not open '%s'", filename);
fprintf(depout, "%s : \\\n", target);
for (i=0; i<s->nb_target_deps; ++i)
fprintf(depout, "\t%s \\\n", s->target_deps[i]);
fprintf(depout, "\n");
fclose(depout);
}