diff options
Diffstat (limited to 'tinyc/libtcc.c')
-rw-r--r-- | tinyc/libtcc.c | 3215 |
1 files changed, 1469 insertions, 1746 deletions
diff --git a/tinyc/libtcc.c b/tinyc/libtcc.c index 0d8702b5f..092c0ba32 100644 --- a/tinyc/libtcc.c +++ b/tinyc/libtcc.c @@ -1,6 +1,6 @@ /* * TCC - Tiny C Compiler - * + * * Copyright (c) 2001-2004 Fabrice Bellard * * This library is free software; you can redistribute it and/or @@ -23,310 +23,115 @@ /********************************************************/ /* global variables */ -/* display benchmark infos */ -int total_lines; -int total_bytes; - -/* parser */ -static struct BufferedFile *file; -static int ch, tok; -static CValue tokc; -static CString tokcstr; /* current parsed string, if any */ -/* additional informations about token */ -static int tok_flags; -#define TOK_FLAG_BOL 0x0001 /* beginning of line before */ -#define TOK_FLAG_BOF 0x0002 /* beginning of file before */ -#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */ -#define TOK_FLAG_EOF 0x0008 /* end of file */ - -static int *macro_ptr, *macro_ptr_allocated; -static int *unget_saved_macro_ptr; -static int unget_saved_buffer[TOK_MAX_SIZE + 1]; -static int unget_buffer_enabled; -static int parse_flags; -#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */ -#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */ -#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a - token. line feed is also - returned at eof */ -#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */ -#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */ - -static Section *text_section, *data_section, *bss_section; /* predefined sections */ -static Section *cur_text_section; /* current section where function code is - generated */ -#ifdef CONFIG_TCC_ASM -static Section *last_text_section; /* to handle .previous asm directive */ -#endif -/* bound check related sections */ -static Section *bounds_section; /* contains global data bound description */ -static Section *lbounds_section; /* contains local data bound description */ -/* symbol sections */ -static Section *symtab_section, *strtab_section; - -/* debug sections */ -static Section *stab_section, *stabstr_section; - -/* loc : local variable index - ind : output code index - rsym: return symbol - anon_sym: anonymous symbol index -*/ -static int rsym, anon_sym, ind, loc; -/* expression generation modifiers */ -static int const_wanted; /* true if constant wanted */ -static int nocode_wanted; /* true if no code generation wanted for an expression */ -static int global_expr; /* true if compound literals must be allocated - globally (used during initializers parsing */ -static CType func_vt; /* current function return type (used by return - instruction) */ -static int func_vc; -static int last_line_num, last_ind, func_ind; /* debug last line number and pc */ -static int tok_ident; -static TokenSym **table_ident; -static TokenSym *hash_ident[TOK_HASH_SIZE]; -static char token_buf[STRING_MAX_SIZE + 1]; -static char *funcname; -static Sym *global_stack, *local_stack; -static Sym *define_stack; -static Sym *global_label_stack, *local_label_stack; -/* symbol allocator */ -#define SYM_POOL_NB (8192 / sizeof(Sym)) -static Sym *sym_free_first; -static void **sym_pools; -static int nb_sym_pools; - -static SValue vstack[VSTACK_SIZE], *vtop; -/* some predefined types */ -static CType char_pointer_type, func_old_type, int_type; - /* use GNU C extensions */ -static int gnu_ext = 1; +ST_DATA int gnu_ext = 1; -/* use Tiny C extensions */ -static int tcc_ext = 1; - -/* max number of callers shown if error */ -#ifdef CONFIG_TCC_BACKTRACE -int num_callers = 6; -const char **rt_bound_error_msg; -#endif +/* use TinyCC extensions */ +ST_DATA int tcc_ext = 1; /* XXX: get rid of this ASAP */ -static struct TCCState *tcc_state; +ST_DATA struct TCCState *tcc_state; -/********************************************************/ -/* function prototypes */ - -/* tccpp.c */ -static void next(void); -char *get_tok_str(int v, CValue *cv); - -/* tccgen.c */ -static void parse_expr_type(CType *type); -static void expr_type(CType *type); -static void unary_type(CType *type); -static void block(int *bsym, int *csym, int *case_sym, int *def_sym, - int case_reg, int is_expr); -static int expr_const(void); -static void expr_eq(void); -static void gexpr(void); -static void gen_inline_functions(void); -static void decl(int l); -static void decl_initializer(CType *type, Section *sec, unsigned long c, - int first, int size_only); -static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, - int has_init, int v, int scope); -int gv(int rc); -void gv2(int rc1, int rc2); -void move_reg(int r, int s); -void save_regs(int n); -void save_reg(int r); -void vpop(void); -void vswap(void); -void vdup(void); -int get_reg(int rc); -int get_reg_ex(int rc,int rc2); - -void gen_op(int op); -void force_charshort_cast(int t); -static void gen_cast(CType *type); -void vstore(void); -static Sym *sym_find(int v); -static Sym *sym_push(int v, CType *type, int r, int c); - -/* type handling */ -static int type_size(CType *type, int *a); -static inline CType *pointed_type(CType *type); -static int pointed_size(CType *type); -static int lvalue_type(int t); -static int parse_btype(CType *type, AttributeDef *ad); -static void type_decl(CType *type, AttributeDef *ad, int *v, int td); -static int compare_types(CType *type1, CType *type2, int unqualified); -static int is_compatible_types(CType *type1, CType *type2); -static int is_compatible_parameter_types(CType *type1, CType *type2); - -int ieee_finite(double d); -void vpushi(int v); -void vpushll(long long v); -void vrott(int n); -void vnrott(int n); -void lexpand_nr(void); -static void vpush_global_sym(CType *type, int v); -void vset(CType *type, int r, int v); -void type_to_str(char *buf, int buf_size, - CType *type, const char *varstr); -static Sym *get_sym_ref(CType *type, Section *sec, - unsigned long offset, unsigned long size); -static Sym *external_global_sym(int v, CType *type, int r); - -/* section generation */ -static void section_realloc(Section *sec, unsigned long new_size); -static void *section_ptr_add(Section *sec, unsigned long size); -static void put_extern_sym(Sym *sym, Section *section, - unsigned long value, unsigned long size); -static void greloc(Section *s, Sym *sym, unsigned long addr, int type); -static int put_elf_str(Section *s, const char *sym); -static int put_elf_sym(Section *s, - unsigned long value, unsigned long size, - int info, int other, int shndx, const char *name); -static int add_elf_sym(Section *s, unsigned long value, unsigned long size, - int info, int other, int sh_num, const char *name); -static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, - int type, int symbol); -static void put_stabs(const char *str, int type, int other, int desc, - unsigned long value); -static void put_stabs_r(const char *str, int type, int other, int desc, - unsigned long value, Section *sec, int sym_index); -static void put_stabn(int type, int other, int desc, int value); -static void put_stabd(int type, int other, int desc); -static int tcc_add_dll(TCCState *s, const char *filename, int flags); - -#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */ -#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */ -#define AFF_PREPROCESS 0x0004 /* preprocess file */ -static int tcc_add_file_internal(TCCState *s, const char *filename, int flags); - -/* tcccoff.c */ -int tcc_output_coff(TCCState *s1, FILE *f); - -/* tccpe.c */ -void *resolve_sym(TCCState *s1, const char *sym, int type); -int pe_load_def_file(struct TCCState *s1, int fd); -int pe_test_res_file(void *v, int size); -int pe_load_res_file(struct TCCState *s1, int fd); -void pe_add_runtime(struct TCCState *s1); -void pe_guess_outfile(char *objfilename, int output_type); -int pe_output_file(struct TCCState *s1, const char *filename); - -/* tccasm.c */ -#ifdef CONFIG_TCC_ASM -static void asm_expr(TCCState *s1, ExprValue *pe); -static int asm_int_expr(TCCState *s1); -static int find_constraint(ASMOperand *operands, int nb_operands, - const char *name, const char **pp); - -static int tcc_assemble(TCCState *s1, int do_preprocess); -#endif - -static void asm_instr(void); -static void asm_global_instr(void); +static int nb_states; /********************************************************/ -/* global variables */ +#if ONE_SOURCE +#include "tccpp.c" +#include "tccgen.c" +#include "tccelf.c" +#include "tccrun.c" #ifdef TCC_TARGET_I386 #include "i386-gen.c" +#include "i386-link.c" +#include "i386-asm.c" #endif - #ifdef TCC_TARGET_ARM #include "arm-gen.c" +#include "arm-link.c" +#include "arm-asm.c" +#endif +#ifdef TCC_TARGET_ARM64 +#include "arm64-gen.c" +#include "arm64-link.c" #endif - #ifdef TCC_TARGET_C67 #include "c67-gen.c" +#include "c67-link.c" +#include "tcccoff.c" #endif - #ifdef TCC_TARGET_X86_64 #include "x86_64-gen.c" +#include "x86_64-link.c" +#include "i386-asm.c" #endif +#ifdef CONFIG_TCC_ASM +#include "tccasm.c" +#endif +#ifdef TCC_TARGET_PE +#include "tccpe.c" +#endif +#endif /* ONE_SOURCE */ -#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) +/********************************************************/ +#ifndef CONFIG_TCC_ASM +ST_FUNC void asm_instr(void) { - return NULL; + tcc_error("inline asm() not supported"); } - -void dlclose(void *p) +ST_FUNC void asm_global_instr(void) { + tcc_error("inline asm() not supported"); } +#endif -const char *dlerror(void) +/********************************************************/ +#ifdef _WIN32 +ST_FUNC char *normalize_slashes(char *path) { - return "error"; + char *p; + for (p = path; *p; ++p) + if (*p == '\\') + *p = '/'; + return path; } -typedef struct TCCSyms { - char *str; - void *ptr; -} TCCSyms; - -#define TCCSYM(a) { #a, &a, }, +static HMODULE tcc_module; -/* 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, int type) +/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */ +static void tcc_set_lib_path_w32(TCCState *s) { - TCCSyms *p; - p = tcc_syms; - while (p->str != NULL) { - if (!strcmp(p->str, symbol)) - return p->ptr; - p++; - } - return NULL; + char path[1024], *p; + GetModuleFileNameA(tcc_module, path, sizeof path); + p = tcc_basename(normalize_slashes(strlwr(path))); + if (p > path) + --p; + *p = 0; + tcc_set_lib_path(s, path); } -#elif !defined(_WIN32) - -#include <dlfcn.h> - -void *resolve_sym(TCCState *s1, const char *sym, int type) +#ifdef TCC_TARGET_PE +static void tcc_add_systemdir(TCCState *s) { - return dlsym(RTLD_DEFAULT, sym); + char buf[1000]; + GetSystemDirectory(buf, sizeof buf); + tcc_add_library_path(s, normalize_slashes(buf)); } - #endif -/********************************************************/ - -/* we use our own 'finite' function to avoid potential problems with - non standard math libs */ -/* XXX: endianness dependent */ -int ieee_finite(double d) +#ifdef LIBTCC_AS_DLL +BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) { - int *p = (int *)&d; - return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; + if (DLL_PROCESS_ATTACH == dwReason) + tcc_module = hDll; + return TRUE; } +#endif +#endif +/********************************************************/ /* copy a string and truncate it. */ -char *pstrcpy(char *buf, int buf_size, const char *s) +ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s) { char *q, *q_end; int c; @@ -346,101 +151,66 @@ char *pstrcpy(char *buf, int buf_size, const char *s) } /* strcat and truncate. */ -char *pstrcat(char *buf, int buf_size, const char *s) +ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s) { int len; len = strlen(buf); - if (len < buf_size) + if (len < buf_size) pstrcpy(buf + len, buf_size - len, s); return buf; } +ST_FUNC char *pstrncpy(char *out, const char *in, size_t num) +{ + memcpy(out, in, num); + out[num] = '\0'; + return out; +} + /* extract the basename of a file */ -char *tcc_basename(const char *name) +PUB_FUNC char *tcc_basename(const char *name) { char *p = strchr(name, 0); - while (p > name && !IS_PATHSEP(p[-1])) + while (p > name && !IS_DIRSEP(p[-1])) --p; return p; } -char *tcc_fileextension (const char *name) +/* 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); } -#ifdef _WIN32 -char *normalize_slashes(char *path) -{ - char *p; - for (p = path; *p; ++p) - if (*p == '\\') - *p = '/'; - return path; -} - -void tcc_set_lib_path_w32(TCCState *s) -{ - /* on win32, we suppose the lib and includes are at the location - of 'tcc.exe' */ - char path[1024], *p; - GetModuleFileNameA(NULL, 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); -} -#endif +/********************************************************/ +/* memory management */ -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 -} +#undef free +#undef malloc +#undef realloc -/* memory management */ -#ifdef MEM_DEBUG -int mem_cur_size; -int mem_max_size; -unsigned malloc_usable_size(void*); -#endif +#ifndef MEM_DEBUG -void tcc_free(void *ptr) +PUB_FUNC void tcc_free(void *ptr) { -#ifdef MEM_DEBUG - mem_cur_size -= malloc_usable_size(ptr); -#endif free(ptr); } -void *tcc_malloc(unsigned long size) +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 + tcc_error("memory full (malloc)"); return ptr; } -void *tcc_mallocz(unsigned long size) +PUB_FUNC void *tcc_mallocz(unsigned long size) { void *ptr; ptr = tcc_malloc(size); @@ -448,23 +218,16 @@ void *tcc_mallocz(unsigned long size) return ptr; } -void *tcc_realloc(void *ptr, unsigned long size) +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 + if (!ptr1 && size) + tcc_error("memory full (realloc)"); return ptr1; } -char *tcc_strdup(const char *str) +PUB_FUNC char *tcc_strdup(const char *str) { char *ptr; ptr = tcc_malloc(strlen(str) + 1); @@ -472,299 +235,229 @@ char *tcc_strdup(const char *str) return ptr; } -#define free(p) use_tcc_free(p) -#define malloc(s) use_tcc_malloc(s) -#define realloc(p, s) use_tcc_realloc(p, s) - -void dynarray_add(void ***ptab, int *nb_ptr, void *data) +PUB_FUNC void tcc_memcheck(void) { - 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; } -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; -} +#else -/* symbol allocator */ -static Sym *__sym_malloc(void) -{ - Sym *sym_pool, *sym, *last_sym; - int i; +#define MEM_DEBUG_MAGIC1 0xFEEDDEB1 +#define MEM_DEBUG_MAGIC2 0xFEEDDEB2 +#define MEM_DEBUG_MAGIC3 0xFEEDDEB3 +#define MEM_DEBUG_FILE_LEN 40 +#define MEM_DEBUG_CHECK3(header) \ + ((mem_debug_header_t*)((char*)header + header->size))->magic3 +#define MEM_USER_PTR(header) \ + ((char *)header + offsetof(mem_debug_header_t, magic3)) +#define MEM_HEADER_PTR(ptr) \ + (mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3)) + +struct mem_debug_header { + unsigned magic1; + unsigned size; + struct mem_debug_header *prev; + struct mem_debug_header *next; + int line_num; + char file_name[MEM_DEBUG_FILE_LEN + 1]; + unsigned magic2; + ALIGNED(16) unsigned magic3; +}; - sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym)); - dynarray_add(&sym_pools, &nb_sym_pools, sym_pool); +typedef struct mem_debug_header mem_debug_header_t; - last_sym = sym_free_first; - sym = sym_pool; - for(i = 0; i < SYM_POOL_NB; i++) { - sym->next = last_sym; - last_sym = sym; - sym++; - } - sym_free_first = last_sym; - return last_sym; -} +static mem_debug_header_t *mem_debug_chain; +static unsigned mem_cur_size; +static unsigned mem_max_size; -static inline Sym *sym_malloc(void) +static mem_debug_header_t *malloc_check(void *ptr, const char *msg) { - Sym *sym; - sym = sym_free_first; - if (!sym) - sym = __sym_malloc(); - sym_free_first = sym->next; - return sym; + mem_debug_header_t * header = MEM_HEADER_PTR(ptr); + if (header->magic1 != MEM_DEBUG_MAGIC1 || + header->magic2 != MEM_DEBUG_MAGIC2 || + MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 || + header->size == (unsigned)-1) { + fprintf(stderr, "%s check failed\n", msg); + if (header->magic1 == MEM_DEBUG_MAGIC1) + fprintf(stderr, "%s:%u: block allocated here.\n", + header->file_name, header->line_num); + exit(1); + } + return header; } -static inline void sym_free(Sym *sym) +PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line) { - sym->next = sym_free_first; - sym_free_first = sym; -} + int ofs; + mem_debug_header_t *header; -Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags) -{ - Section *sec; + header = malloc(sizeof(mem_debug_header_t) + size); + if (!header) + tcc_error("memory full (malloc)"); - 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; - } + header->magic1 = MEM_DEBUG_MAGIC1; + header->magic2 = MEM_DEBUG_MAGIC2; + header->size = size; + MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3; + header->line_num = line; + ofs = strlen(file) - MEM_DEBUG_FILE_LEN; + strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN); + header->file_name[MEM_DEBUG_FILE_LEN] = 0; - 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); - } + header->next = mem_debug_chain; + header->prev = NULL; + if (header->next) + header->next->prev = header; + mem_debug_chain = header; - return sec; -} + mem_cur_size += size; + if (mem_cur_size > mem_max_size) + mem_max_size = mem_cur_size; -static void free_section(Section *s) -{ - tcc_free(s->data); + return MEM_USER_PTR(header); } -/* realloc section and set its content to zero */ -static void section_realloc(Section *sec, unsigned long new_size) +PUB_FUNC void tcc_free_debug(void *ptr) { - 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; + mem_debug_header_t *header; + if (!ptr) + return; + header = malloc_check(ptr, "tcc_free"); + mem_cur_size -= header->size; + header->size = (unsigned)-1; + if (header->next) + header->next->prev = header->prev; + if (header->prev) + header->prev->next = header->next; + if (header == mem_debug_chain) + mem_debug_chain = header->next; + free(header); } -/* reserve at least 'size' bytes in section 'sec' from - sec->data_offset. */ -static void *section_ptr_add(Section *sec, unsigned long size) +PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line) { - unsigned long offset, offset1; + void *ptr; + ptr = tcc_malloc_debug(size,file,line); + memset(ptr, 0, size); + return ptr; +} - offset = sec->data_offset; - offset1 = offset + size; - if (offset1 > sec->data_allocated) - section_realloc(sec, offset1); - sec->data_offset = offset1; - return sec->data + offset; +PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line) +{ + mem_debug_header_t *header; + int mem_debug_chain_update = 0; + if (!ptr) + return tcc_malloc_debug(size, file, line); + header = malloc_check(ptr, "tcc_realloc"); + mem_cur_size -= header->size; + mem_debug_chain_update = (header == mem_debug_chain); + header = realloc(header, sizeof(mem_debug_header_t) + size); + if (!header) + tcc_error("memory full (realloc)"); + header->size = size; + MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3; + if (header->next) + header->next->prev = header; + if (header->prev) + header->prev->next = header; + if (mem_debug_chain_update) + mem_debug_chain = header; + mem_cur_size += size; + if (mem_cur_size > mem_max_size) + mem_max_size = mem_cur_size; + return MEM_USER_PTR(header); } -/* return a reference to a section, and create it if it does not - exists */ -Section *find_section(TCCState *s1, const char *name) +PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line) { - 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); + char *ptr; + ptr = tcc_malloc_debug(strlen(str) + 1, file, line); + strcpy(ptr, str); + return ptr; } -/* update sym->c so that it points to an external symbol in section - 'section' with value 'value' */ -static void put_extern_sym2(Sym *sym, Section *section, - unsigned long value, unsigned long size, - int can_add_underscore) +PUB_FUNC void tcc_memcheck(void) { - int sym_type, sym_bind, sh_num, info, other, attr; - 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; - - other = attr = 0; - - if ((sym->type.t & VT_BTYPE) == VT_FUNC) { - sym_type = STT_FUNC; -#ifdef TCC_TARGET_PE - if (sym->type.ref) - attr = sym->type.ref->r; - if (FUNC_EXPORT(attr)) - other |= 1; - if (FUNC_CALL(attr) == FUNC_STDCALL) - other |= 2; -#endif - } else { - sym_type = STT_OBJECT; - } - - if (sym->type.t & VT_STATIC) - sym_bind = STB_LOCAL; - 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) { -#if 0 - /* 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; - } + if (mem_cur_size) { + mem_debug_header_t *header = mem_debug_chain; + fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n", + mem_cur_size, mem_max_size); + while (header) { + fprintf(stderr, "%s:%u: error: %u bytes leaked\n", + header->file_name, header->line_num, header->size); + header = header->next; } +#if MEM_DEBUG-0 == 2 + exit(2); #endif - -#ifdef TCC_TARGET_PE - if ((other & 2) && can_add_underscore) { - sprintf(buf1, "_%s@%d", name, FUNC_ARGS(attr)); - name = buf1; - } else -#endif - if (tcc_state->leading_underscore && can_add_underscore) { - buf1[0] = '_'; - pstrcpy(buf1 + 1, sizeof(buf1) - 1, name); - name = buf1; - } - 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; - esym->st_other |= other; } } +#endif /* MEM_DEBUG */ -static void put_extern_sym(Sym *sym, Section *section, - unsigned long value, unsigned long size) -{ - put_extern_sym2(sym, section, value, size, 1); -} +#define free(p) use_tcc_free(p) +#define malloc(s) use_tcc_malloc(s) +#define realloc(p, s) use_tcc_realloc(p, s) -/* add a new relocation entry to symbol 'sym' in section 's' */ -static void greloc(Section *s, Sym *sym, unsigned long offset, int type) -{ - if (!sym->c) - put_extern_sym(sym, NULL, 0, 0); - /* now we can add ELF relocation info */ - put_elf_reloc(symtab_section, s, offset, type, sym->c); -} +/********************************************************/ +/* dynarrays */ -static inline int isid(int c) +ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data) { - return (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - c == '_'; -} + int nb, nb_alloc; + void **pp; -static inline int isnum(int c) -{ - return c >= '0' && c <= '9'; + nb = *nb_ptr; + pp = *(void ***)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 *)); + *(void***)ptab = pp; + } + pp[nb++] = data; + *nb_ptr = nb; } -static inline int isoct(int c) +ST_FUNC void dynarray_reset(void *pp, int *n) { - return c >= '0' && c <= '7'; + void **p; + for (p = *(void***)pp; *n; ++p, --*n) + if (*p) + tcc_free(*p); + tcc_free(*(void**)pp); + *(void**)pp = NULL; } -static inline int toup(int c) +static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in) { - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - else - return c; + const char *p; + do { + int c; + CString str; + + cstr_new(&str); + for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; ++p) { + if (c == '{' && p[1] && p[2] == '}') { + c = p[1], p += 2; + if (c == 'B') + cstr_cat(&str, s->tcc_lib_path, -1); + } else { + cstr_ccat(&str, c); + } + } + if (str.size) { + cstr_ccat(&str, '\0'); + dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data)); + } + cstr_free(&str); + in = p+1; + } while (*p); } +/********************************************************/ + static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap) { int len; @@ -780,42 +473,51 @@ static void strcat_printf(char *buf, int buf_size, const char *fmt, ...) va_end(ap); } -void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) +static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) { char buf[2048]; - BufferedFile **f; - + BufferedFile **pf, *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); + /* use upper file if inline ":asm:" or token ":paste:" */ + for (f = file; f && f->filename[0] == ':'; f = f->prev) + ; + if (f) { + for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++) + strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n", + (*pf)->filename, (*pf)->line_num); + if (f->line_num > 0) { + strcat_printf(buf, sizeof(buf), "%s:%d: ", + f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL)); } else { - strcat_printf(buf, sizeof(buf), - "%s: ", file->filename); + strcat_printf(buf, sizeof(buf), "%s: ", + f->filename); } } else { - strcat_printf(buf, sizeof(buf), - "tcc: "); + 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 */ + if (s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout) + /* print a newline during tcc -E */ + printf("\n"), fflush(stdout); + fflush(stdout); /* flush -v output */ fprintf(stderr, "%s\n", buf); - } - if (!is_warning || s1->warn_error) { + fflush(stderr); /* print error/warning now (win32) */ + } else { s1->error_func(s1->error_opaque, buf); - s1->nb_errors++; } + if (!is_warning || s1->warn_error) + s1->nb_errors++; } -void tcc_set_error_func(TCCState *s, void *error_opaque, +LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, void (*error_func)(void *opaque, const char *msg)) { s->error_opaque = error_opaque; @@ -823,7 +525,7 @@ void tcc_set_error_func(TCCState *s, void *error_opaque, } /* error without aborting current compilation */ -void error_noabort(const char *fmt, ...) +PUB_FUNC void tcc_error_noabort(const char *fmt, ...) { TCCState *s1 = tcc_state; va_list ap; @@ -833,7 +535,7 @@ void error_noabort(const char *fmt, ...) va_end(ap); } -void error(const char *fmt, ...) +PUB_FUNC void tcc_error(const char *fmt, ...) { TCCState *s1 = tcc_state; va_list ap; @@ -850,12 +552,7 @@ void error(const char *fmt, ...) } } -void expect(const char *msg) -{ - error("%s expected", msg); -} - -void warning(const char *fmt, ...) +PUB_FUNC void tcc_warning(const char *fmt, ...) { TCCState *s1 = tcc_state; va_list ap; @@ -868,435 +565,135 @@ void warning(const char *fmt, ...) va_end(ap); } -void skip(int c) -{ - if (tok != c) - error("'%c' expected", c); - next(); -} - -static void test_lvalue(void) -{ - if (!(vtop->r & VT_LVAL)) - expect("lvalue"); -} - -/* CString handling */ - -static void cstr_realloc(CString *cstr, int new_size) -{ - int size; - void *data; - - size = cstr->size_allocated; - if (size == 0) - size = 8; /* no need to allocate a too small first string */ - while (size < new_size) - size = size * 2; - data = tcc_realloc(cstr->data_allocated, size); - if (!data) - error("memory full"); - cstr->data_allocated = data; - cstr->size_allocated = size; - cstr->data = data; -} - -/* add a byte */ -static inline void cstr_ccat(CString *cstr, int ch) -{ - int size; - size = cstr->size + 1; - if (size > cstr->size_allocated) - cstr_realloc(cstr, size); - ((unsigned char *)cstr->data)[size - 1] = ch; - cstr->size = size; -} - -static void cstr_cat(CString *cstr, const char *str) -{ - int c; - for(;;) { - c = *str; - if (c == '\0') - break; - cstr_ccat(cstr, c); - str++; - } -} - -/* add a wide char */ -static void cstr_wccat(CString *cstr, int ch) -{ - int size; - size = cstr->size + sizeof(nwchar_t); - if (size > cstr->size_allocated) - cstr_realloc(cstr, size); - *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch; - cstr->size = size; -} - -static void cstr_new(CString *cstr) -{ - memset(cstr, 0, sizeof(CString)); -} - -/* free string and reset it to NULL */ -static void cstr_free(CString *cstr) -{ - tcc_free(cstr->data_allocated); - cstr_new(cstr); -} - -#define cstr_reset(cstr) cstr_free(cstr) - -/* XXX: unicode ? */ -static void add_char(CString *cstr, int c) -{ - if (c == '\'' || c == '\"' || c == '\\') { - /* XXX: could be more precise if char or string */ - cstr_ccat(cstr, '\\'); - } - if (c >= 32 && c <= 126) { - cstr_ccat(cstr, c); - } else { - cstr_ccat(cstr, '\\'); - if (c == '\n') { - cstr_ccat(cstr, 'n'); - } else { - cstr_ccat(cstr, '0' + ((c >> 6) & 7)); - cstr_ccat(cstr, '0' + ((c >> 3) & 7)); - cstr_ccat(cstr, '0' + (c & 7)); - } - } -} - -/* push, without hashing */ -static Sym *sym_push2(Sym **ps, int v, int t, long c) -{ - Sym *s; - s = sym_malloc(); - s->v = v; - s->type.t = t; - s->c = c; - s->next = NULL; - /* add in stack */ - s->prev = *ps; - *ps = s; - return s; -} - -/* find a symbol and return its associated structure. 's' is the top - of the symbol stack */ -static Sym *sym_find2(Sym *s, int v) -{ - while (s) { - if (s->v == v) - return s; - s = s->prev; - } - return NULL; -} - -/* structure lookup */ -static inline Sym *struct_find(int v) -{ - v -= TOK_IDENT; - if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) - return NULL; - return table_ident[v]->sym_struct; -} - -/* find an identifier */ -static inline Sym *sym_find(int v) -{ - v -= TOK_IDENT; - if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) - return NULL; - return table_ident[v]->sym_identifier; -} +/********************************************************/ +/* I/O layer */ -/* push a given symbol on the symbol stack */ -static Sym *sym_push(int v, CType *type, int r, int c) +ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen) { - Sym *s, **ps; - TokenSym *ts; - - if (local_stack) - ps = &local_stack; - else - ps = &global_stack; - s = sym_push2(ps, v, type->t, c); - s->type.ref = type->ref; - s->r = r; - /* don't record fields or anonymous symbols */ - /* XXX: simplify */ - if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { - /* record symbol in token array */ - ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT]; - if (v & SYM_STRUCT) - ps = &ts->sym_struct; - else - ps = &ts->sym_identifier; - s->prev_tok = *ps; - *ps = s; - } - return s; -} + BufferedFile *bf; + int buflen = initlen ? initlen : IO_BUF_SIZE; -/* push a global identifier */ -static Sym *global_identifier_push(int v, int t, int c) -{ - Sym *s, **ps; - s = sym_push2(&global_stack, v, t, c); - /* don't record anonymous symbol */ - if (v < SYM_FIRST_ANOM) { - ps = &table_ident[v - TOK_IDENT]->sym_identifier; - /* modify the top most local identifier, so that - sym_identifier will point to 's' when popped */ - while (*ps != NULL) - ps = &(*ps)->prev_tok; - s->prev_tok = NULL; - *ps = s; - } - return s; + bf = tcc_mallocz(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); + bf->true_filename = bf->filename; + bf->line_num = 1; + bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; + bf->fd = -1; + bf->prev = file; + file = bf; + tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; } -/* pop symbols until top reaches 'b' */ -static void sym_pop(Sym **ptop, Sym *b) +ST_FUNC void tcc_close(void) { - Sym *s, *ss, **ps; - TokenSym *ts; - int v; - - s = *ptop; - while(s != b) { - ss = s->prev; - v = s->v; - /* remove symbol in token array */ - /* XXX: simplify */ - if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { - ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT]; - if (v & SYM_STRUCT) - ps = &ts->sym_struct; - else - ps = &ts->sym_identifier; - *ps = s->prev_tok; - } - sym_free(s); - s = ss; + BufferedFile *bf = file; + if (bf->fd > 0) { + close(bf->fd); + total_lines += bf->line_num; } - *ptop = b; + if (bf->true_filename != bf->filename) + tcc_free(bf->true_filename); + file = bf->prev; + tcc_free(bf); } -/* I/O layer */ - -BufferedFile *tcc_open(TCCState *s1, const char *filename) +ST_FUNC int tcc_open(TCCState *s1, const char *filename) { int fd; - BufferedFile *bf; - if (strcmp(filename, "-") == 0) - fd = 0, filename = "stdin"; + 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":"->", - (s1->include_stack_ptr - s1->include_stack), "", filename); + (int)(s1->include_stack_ptr - s1->include_stack), "", filename); if (fd < 0) - return NULL; - bf = tcc_malloc(sizeof(BufferedFile)); - bf->fd = fd; - bf->buf_ptr = bf->buffer; - bf->buf_end = bf->buffer; - bf->buffer[0] = CH_EOB; /* put eob symbol */ - pstrcpy(bf->filename, sizeof(bf->filename), filename); + return -1; + tcc_open_bf(s1, filename, 0); #ifdef _WIN32 - normalize_slashes(bf->filename); + normalize_slashes(file->filename); #endif - bf->line_num = 1; - bf->ifndef_macro = 0; - bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; - // printf("opening '%s'\n", filename); - return bf; + file->fd = fd; + return fd; } -void tcc_close(BufferedFile *bf) -{ - total_lines += bf->line_num; - close(bf->fd); - tcc_free(bf); -} - -#include "tccpp.c" -#include "tccgen.c" - - -/* compile the C file opened in 'file'. Return non zero if errors. */ +/* compile the file opened in 'file'. Return non zero if errors. */ static int tcc_compile(TCCState *s1) { Sym *define_start; - 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 + int filetype, is_asm; define_start = define_stack; - nocode_wanted = 1; + filetype = s1->filetype; + is_asm = filetype == AFF_TYPE_ASM || filetype == AFF_TYPE_ASMPP; 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; - next(); - decl(VT_CONST); - if (tok != TOK_EOF) - expect("declaration"); - - /* 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); + preprocess_start(s1, is_asm); + if (s1->output_type == TCC_OUTPUT_PREPROCESS) { + tcc_preprocess(s1); + } else if (is_asm) { +#ifdef CONFIG_TCC_ASM + tcc_assemble(s1, filetype == AFF_TYPE_ASMPP); +#else + tcc_error_noabort("asm not supported"); +#endif + } else { + tccgen_compile(s1); } } 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); - + preprocess_end(s1); + free_inline_functions(s1); + /* reset define stack, but keep -D and built-ins */ + free_defines(define_start); + sym_pop(&global_stack, NULL, 0); + sym_pop(&local_stack, NULL, 0); return s1->nb_errors != 0 ? -1 : 0; } -int tcc_compile_string(TCCState *s, const char *str) +LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str) { - BufferedFile bf1, *bf = &bf1; - int ret, len; - char *buf; + int len, ret; - /* init file structure */ - bf->fd = -1; - /* XXX: avoid copying */ len = strlen(str); - buf = tcc_malloc(len + 1); - if (!buf) - return -1; - memcpy(buf, str, len); - buf[len] = CH_EOB; - bf->buf_ptr = buf; - bf->buf_end = buf + len; - pstrcpy(bf->filename, sizeof(bf->filename), "<string>"); - bf->line_num = 1; - file = bf; + tcc_open_bf(s, "<string>", len); + memcpy(file->buffer, str, len); ret = tcc_compile(s); - file = NULL; - tcc_free(buf); - - /* currently, no need to close */ + tcc_close(); return ret; } /* define a preprocessor symbol. A value can also be provided with the '=' operator */ -void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) +LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) { - BufferedFile bf1, *bf = &bf1; - - pstrcpy(bf->buffer, IO_BUF_SIZE, sym); - pstrcat(bf->buffer, IO_BUF_SIZE, " "); + int len1, len2; /* default value */ - if (!value) + if (!value) value = "1"; - pstrcat(bf->buffer, IO_BUF_SIZE, value); - + len1 = strlen(sym); + len2 = strlen(value); + /* init file structure */ - bf->fd = -1; - bf->buf_ptr = bf->buffer; - bf->buf_end = bf->buffer + strlen(bf->buffer); - *bf->buf_end = CH_EOB; - bf->filename[0] = '\0'; - bf->line_num = 1; - file = bf; - - s1->include_stack_ptr = s1->include_stack; + 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(); - file = NULL; + tcc_close(); } /* undefine a preprocessor symbol */ -void tcc_undefine_symbol(TCCState *s1, const char *sym) +LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym) { TokenSym *ts; Sym *s; @@ -1307,458 +704,22 @@ void tcc_undefine_symbol(TCCState *s1, const char *sym) define_undef(s); } -#ifdef CONFIG_TCC_ASM - -#ifdef TCC_TARGET_I386 -#include "i386-asm.c" -#endif -#include "tccasm.c" - -#else -static void asm_instr(void) -{ - error("inline asm() not supported"); -} -static void asm_global_instr(void) -{ - error("inline asm() not supported"); -} -#endif - -#include "tccelf.c" - -#ifdef TCC_TARGET_COFF -#include "tcccoff.c" -#endif - -#ifdef TCC_TARGET_PE -#include "tccpe.c" -#endif - -#ifdef CONFIG_TCC_BACKTRACE -/* print the position in the source file of PC value 'pc' by reading - the stabs debug information */ -static void 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); - goto found; - } - } - } - } - /* did not find any info: */ - fprintf(stderr, " ???\n"); - return; - 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"); -} - -#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 */ - *paddr = uc->uc_mcontext.gregs[REG_RIP]; - return 0; - } else { - fp = uc->uc_mcontext.gregs[REG_RBP]; - 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 "); - rt_printline(pc); - } - 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, mem; - int i; - - if (0 == s1->runtime_added) { - s1->runtime_added = 1; - s1->nb_errors = 0; -#ifdef TCC_TARGET_PE - pe_add_runtime(s1); - relocate_common_syms(); - tcc_add_linker_symbols(s1); -#else - tcc_add_runtime(s1); - relocate_common_syms(); - tcc_add_linker_symbols(s1); - build_got_entries(s1); -#endif - } - - offset = 0, mem = (unsigned long)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; - -#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 - - 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*)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 TCC_TARGET_X86_64 - set_pages_executable(s1->runtime_plt_and_got, - s1->runtime_plt_and_got_offset); -#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); - - /* 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 = (void *)tcc_get_symbol_err(s1, "__bound_init"); - bound_init(); - } -#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 -} - +/* cleanup all static data used during compilation */ static void tcc_cleanup(void) { - int i, n; - if (NULL == tcc_state) return; + while (file) + tcc_close(); + tccpp_delete(tcc_state); 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; } -TCCState *tcc_new(void) +LIBTCCAPI TCCState *tcc_new(void) { TCCState *s; @@ -1768,10 +729,30 @@ TCCState *tcc_new(void) if (!s) return NULL; tcc_state = s; - s->output_type = TCC_OUTPUT_MEMORY; - s->tcc_lib_path = CONFIG_TCCDIR; + ++nb_states; + + s->alacarte_link = 1; + s->nocommon = 1; + s->warn_implicit_function_declaration = 1; + s->ms_extensions = 1; - preprocess_new(); +#ifdef CHAR_IS_UNSIGNED + s->char_is_unsigned = 1; +#endif +#ifdef TCC_TARGET_I386 + s->seg_size = 32; +#endif + /* enable this if you want symbols with leading underscore on windows: */ +#if 0 /* def TCC_TARGET_PE */ + s->leading_underscore = 1; +#endif +#ifdef _WIN32 + tcc_set_lib_path_w32(s); +#else + tcc_set_lib_path(s, CONFIG_TCCDIR); +#endif + tccelf_new(s); + tccpp_new(s); /* we add dummy defines for some special macros to speed up tests and to have working defined() */ @@ -1779,17 +760,28 @@ TCCState *tcc_new(void) define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL); define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL); define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL); + define_push(TOK___COUNTER__, MACRO_OBJ, NULL, NULL); + { + /* define __TINYC__ 92X */ + char buffer[32]; int a,b,c; + sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c); + sprintf(buffer, "%d", a*10000 + b*100 + c); + tcc_define_symbol(s, "__TINYC__", buffer); + } /* standard defines */ tcc_define_symbol(s, "__STDC__", NULL); tcc_define_symbol(s, "__STDC_VERSION__", "199901L"); + tcc_define_symbol(s, "__STDC_HOSTED__", NULL); + + /* target defines */ #if defined(TCC_TARGET_I386) tcc_define_symbol(s, "__i386__", NULL); -#endif -#if defined(TCC_TARGET_X86_64) + tcc_define_symbol(s, "__i386", NULL); + tcc_define_symbol(s, "i386", NULL); +#elif defined(TCC_TARGET_X86_64) tcc_define_symbol(s, "__x86_64__", NULL); -#endif -#if defined(TCC_TARGET_ARM) +#elif 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); @@ -1798,372 +790,403 @@ TCCState *tcc_new(void) tcc_define_symbol(s, "__arm", NULL); tcc_define_symbol(s, "arm", NULL); tcc_define_symbol(s, "__APCS_32__", NULL); + tcc_define_symbol(s, "__ARMEL__", NULL); +#if defined(TCC_ARM_EABI) + tcc_define_symbol(s, "__ARM_EABI__", NULL); +#endif +#if defined(TCC_ARM_HARDFLOAT) + s->float_abi = ARM_HARD_FLOAT; + tcc_define_symbol(s, "__ARM_PCS_VFP", NULL); +#else + s->float_abi = ARM_SOFTFP_FLOAT; #endif +#elif defined(TCC_TARGET_ARM64) + tcc_define_symbol(s, "__aarch64__", NULL); +#elif defined TCC_TARGET_C67 + tcc_define_symbol(s, "__C67__", 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); -#if defined(__linux) + tcc_define_symbol(s, "unix", NULL); +# if defined(__linux__) tcc_define_symbol(s, "__linux__", NULL); tcc_define_symbol(s, "__linux", NULL); +# endif +# if defined(__FreeBSD__) + tcc_define_symbol(s, "__FreeBSD__", "__FreeBSD__"); + /* No 'Thread Storage Local' on FreeBSD with tcc */ + tcc_define_symbol(s, "__NO_TLS", NULL); +# endif +# if defined(__FreeBSD_kernel__) + tcc_define_symbol(s, "__FreeBSD_kernel__", NULL); +# endif +# if defined(__NetBSD__) + tcc_define_symbol(s, "__NetBSD__", "__NetBSD__"); +# endif +# if defined(__OpenBSD__) + tcc_define_symbol(s, "__OpenBSD__", "__OpenBSD__"); +# endif #endif -#endif - /* tiny C specific defines */ - tcc_define_symbol(s, "__TINYC__", NULL); - /* tiny C & gcc defines */ + /* TinyCC & gcc defines */ +#if PTR_SIZE == 4 + /* 32bit systems. */ tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int"); tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int"); + tcc_define_symbol(s, "__ILP32__", NULL); +#elif LONG_SIZE == 4 + /* 64bit Windows. */ + tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long"); + tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long"); + tcc_define_symbol(s, "__LLP64__", NULL); +#else + /* Other 64bit systems. */ + tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long"); + tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long"); + tcc_define_symbol(s, "__LP64__", NULL); +#endif + #ifdef TCC_TARGET_PE tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short"); + tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short"); #else tcc_define_symbol(s, "__WCHAR_TYPE__", "int"); -#endif - -#ifndef TCC_TARGET_PE - /* default library paths */ - tcc_add_library_path(s, CONFIG_SYSROOT "/usr/local/lib"); - tcc_add_library_path(s, CONFIG_SYSROOT "/usr/lib"); - tcc_add_library_path(s, CONFIG_SYSROOT "/lib"); -#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; - -#ifdef CHAR_IS_UNSIGNED - s->char_is_unsigned = 1; -#endif -#if defined(TCC_TARGET_PE) && 0 - /* XXX: currently the PE linker is not ready to support that */ - s->leading_underscore = 1; -#endif + /* wint_t is unsigned int by default, but (signed) int on BSDs + and unsigned short on windows. Other OSes might have still + other conventions, sigh. */ +# if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \ + || defined(__NetBSD__) || defined(__OpenBSD__) + tcc_define_symbol(s, "__WINT_TYPE__", "int"); +# ifdef __FreeBSD__ + /* define __GNUC__ to have some useful stuff from sys/cdefs.h + that are unconditionally used in FreeBSDs other system headers :/ */ + tcc_define_symbol(s, "__GNUC__", "2"); + tcc_define_symbol(s, "__GNUC_MINOR__", "7"); + tcc_define_symbol(s, "__builtin_alloca", "alloca"); +# endif +# else + tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int"); + /* glibc defines */ + tcc_define_symbol(s, "__REDIRECT(name, proto, alias)", + "name proto __asm__ (#alias)"); + tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)", + "name proto __asm__ (#alias) __THROW"); +# endif +# if defined(TCC_MUSL) + tcc_define_symbol(s, "__DEFINED_va_list", ""); + tcc_define_symbol(s, "__DEFINED___isoc_va_list", ""); + tcc_define_symbol(s, "__isoc_va_list", "void *"); +# endif /* TCC_MUSL */ + /* Some GCC builtins that are simple to express as macros. */ + tcc_define_symbol(s, "__builtin_extract_return_addr(x)", "x"); +#endif /* ndef TCC_TARGET_PE */ return s; } -void tcc_delete(TCCState *s1) +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 sections */ + tccelf_delete(s1); /* free library paths */ dynarray_reset(&s1->library_paths, &s1->nb_library_paths); + dynarray_reset(&s1->crt_paths, &s1->nb_crt_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); + dynarray_reset(&s1->cmd_include_files, &s1->nb_cmd_include_files); + + tcc_free(s1->tcc_lib_path); + tcc_free(s1->soname); + tcc_free(s1->rpath); + tcc_free(s1->init_symbol); + tcc_free(s1->fini_symbol); + tcc_free(s1->outfile); + tcc_free(s1->deps_outfile); + dynarray_reset(&s1->files, &s1->nb_files); + dynarray_reset(&s1->target_deps, &s1->nb_target_deps); + dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs); + dynarray_reset(&s1->argv, &s1->argc); + +#ifdef TCC_IS_NATIVE + /* free runtime memory */ + tcc_run_free(s1); +#endif tcc_free(s1); + if (0 == --nb_states) + tcc_memcheck(); } -int tcc_add_include_path(TCCState *s1, const char *pathname) +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) { - char *pathname1; - - pathname1 = tcc_strdup(pathname); - dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1); + s->output_type = output_type; + + /* always elf for objects */ + if (output_type == TCC_OUTPUT_OBJ) + s->output_format = TCC_OUTPUT_FORMAT_ELF; + + if (s->char_is_unsigned) + tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL); + + if (!s->nostdinc) { + /* default include paths */ + /* -isystem paths have already been handled */ + tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS); + } + +#ifdef CONFIG_TCC_BCHECK + if (s->do_bounds_check) { + /* if bound checking, then add corresponding sections */ + tccelf_bounds_new(s); + /* define symbol */ + tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL); + } +#endif + if (s->do_debug) { + /* add debug sections */ + tccelf_stab_new(s); + } + + tcc_add_library_path(s, CONFIG_TCC_LIBPATHS); + +#ifdef TCC_TARGET_PE +# ifdef _WIN32 + if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ) + tcc_add_systemdir(s); +# endif +#else + /* paths for crt objects */ + tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX); + /* add libc crt1/crti objects */ + if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) && + !s->nostdlib) { + if (output_type != TCC_OUTPUT_DLL) + tcc_add_crt(s, "crt1.o"); + tcc_add_crt(s, "crti.o"); + } +#endif return 0; } -int tcc_add_sysinclude_path(TCCState *s1, const char *pathname) +LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname) { - char *pathname1; - - pathname1 = tcc_strdup(pathname); - dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1); + tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname); return 0; } -static int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) +LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname) { - const char *ext; - ElfW(Ehdr) ehdr; - int fd, ret; - BufferedFile *saved_file; + tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname); + return 0; +} - /* find source file type with extension */ - ext = tcc_fileextension(filename); - if (ext[0]) - ext++; +ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) +{ + int ret; /* open the file */ - saved_file = file; - file = tcc_open(s1, filename); - if (!file) { - if (flags & AFF_PRINT_ERROR) { - error_noabort("file '%s' not found", filename); - } - ret = -1; - goto fail1; + ret = tcc_open(s1, filename); + if (ret < 0) { + if (flags & AFF_PRINT_ERROR) + tcc_error_noabort("file '%s' not found", filename); + return ret; } - if (flags & AFF_PREPROCESS) { - ret = tcc_preprocess(s1); - } else if (!ext[0] || !PATHCMP(ext, "c")) { - /* C file assumed */ - ret = tcc_compile(s1); - } else -#ifdef CONFIG_TCC_ASM - if (!strcmp(ext, "S")) { - /* preprocessed assembler */ - ret = tcc_assemble(s1, 1); - } else if (!strcmp(ext, "s")) { - /* non preprocessed assembler */ - ret = tcc_assemble(s1, 0); - } else -#endif -#ifdef TCC_TARGET_PE - if (!PATHCMP(ext, "def")) { - ret = pe_load_def_file(s1, file->fd); - } else -#endif - { + /* update target deps */ + dynarray_add(&s1->target_deps, &s1->nb_target_deps, + tcc_strdup(filename)); + + if (flags & AFF_TYPE_BIN) { + ElfW(Ehdr) ehdr; + int fd, obj_type; + fd = file->fd; - /* assume executable format: auto guess file type */ - ret = read(fd, &ehdr, sizeof(ehdr)); + obj_type = tcc_object_type(fd, &ehdr); lseek(fd, 0, SEEK_SET); - if (ret <= 0) { - error_noabort("could not read header"); - goto fail; - } else if (ret != sizeof(ehdr)) { - goto try_load_script; - } - if (ehdr.e_ident[0] == ELFMAG0 && - ehdr.e_ident[1] == ELFMAG1 && - ehdr.e_ident[2] == ELFMAG2 && - ehdr.e_ident[3] == ELFMAG3) { - file->line_num = 0; /* do not display line number if error */ - if (ehdr.e_type == ET_REL) { - ret = tcc_load_object_file(s1, fd, 0); - } else if (ehdr.e_type == ET_DYN) { - if (s1->output_type == TCC_OUTPUT_MEMORY) { -#ifdef TCC_TARGET_PE + /* do not display line number if error */ + file->line_num = 0; + +#ifdef TCC_TARGET_MACHO + if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), ".dylib")) + obj_type = AFF_BINTYPE_DYN; +#endif + + switch (obj_type) { + case AFF_BINTYPE_REL: + ret = tcc_load_object_file(s1, fd, 0); + break; +#ifndef TCC_TARGET_PE + case AFF_BINTYPE_DYN: + if (s1->output_type == TCC_OUTPUT_MEMORY) { + ret = 0; +#ifdef TCC_IS_NATIVE + if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY)) ret = -1; -#else - void *h; - h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); - if (h) - ret = 0; - else - ret = -1; #endif - } else { - ret = tcc_load_dll(s1, fd, filename, - (flags & AFF_REFERENCED_DLL) != 0); - } } else { - error_noabort("unrecognized ELF file"); - goto fail; + ret = tcc_load_dll(s1, fd, filename, + (flags & AFF_REFERENCED_DLL) != 0); } - } else if (memcmp((char *)&ehdr, ARMAG, 8) == 0) { - file->line_num = 0; /* do not display line number if error */ + break; +#endif + case AFF_BINTYPE_AR: ret = tcc_load_archive(s1, fd); - } else + break; #ifdef TCC_TARGET_COFF - if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) { + case AFF_BINTYPE_C67: ret = tcc_load_coff(s1, fd); - } else + break; #endif + default: #ifdef TCC_TARGET_PE - if (pe_test_res_file(&ehdr, ret)) { - ret = pe_load_res_file(s1, fd); - } else -#endif - { + ret = pe_load_file(s1, filename, fd); +#else /* as GNU ld, consider it is an ld script if not recognized */ - try_load_script: ret = tcc_load_ldscript(s1); - if (ret < 0) { - error_noabort("unrecognized file type"); - goto fail; - } +#endif + if (ret < 0) + tcc_error_noabort("unrecognized file type"); + break; } + } else { + ret = tcc_compile(s1); } - the_end: - tcc_close(file); - fail1: - file = saved_file; + tcc_close(); return ret; - fail: - ret = -1; - goto the_end; } -int tcc_add_file(TCCState *s, const char *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_file(TCCState *s, const char *filename) +{ + int filetype = s->filetype; + int flags = AFF_PRINT_ERROR; + if (filetype == 0) { + /* use a file extension to detect a filetype */ + const char *ext = tcc_fileextension(filename); + if (ext[0]) { + ext++; + if (!strcmp(ext, "S")) + filetype = AFF_TYPE_ASMPP; + else if (!strcmp(ext, "s")) + filetype = AFF_TYPE_ASM; + else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i")) + filetype = AFF_TYPE_C; + else + flags |= AFF_TYPE_BIN; + } else { + filetype = AFF_TYPE_C; + } + s->filetype = filetype; + } + return tcc_add_file_internal(s, filename, flags); } -int tcc_add_library_path(TCCState *s, const char *pathname) +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); + tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname); return 0; } -/* find and load a dll. Return non zero if not found */ -/* XXX: add '-rpath' option support ? */ -static int tcc_add_dll(TCCState *s, const char *filename, int flags) +static int tcc_add_library_internal(TCCState *s, const char *fmt, + const char *filename, int flags, char **paths, int nb_paths) { 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) + for(i = 0; i < nb_paths; i++) { + snprintf(buf, sizeof(buf), fmt, paths[i], filename); + if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0) return 0; } return -1; } -/* the library name is the same as the argument of the '-l' option */ -int tcc_add_library(TCCState *s, const char *libraryname) +/* 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; - - /* first we look for the dynamic library if not static linking */ - if (!s->static_link) { -#ifdef TCC_TARGET_PE - snprintf(buf, sizeof(buf), "%s.def", libraryname); + return tcc_add_library_internal(s, "%s/%s", filename, flags, + s->library_paths, s->nb_library_paths); +} + +ST_FUNC int tcc_add_crt(TCCState *s, const char *filename) +{ + if (-1 == tcc_add_library_internal(s, "%s/%s", + filename, 0, s->crt_paths, s->nb_crt_paths)) + tcc_error_noabort("file '%s' not found", filename); + return 0; +} + +/* the library name is the same as the argument of the '-l' option */ +LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) +{ +#if defined TCC_TARGET_PE + const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL }; + const char **pp = s->static_link ? libs + 4 : libs; +#elif defined TCC_TARGET_MACHO + const char *libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL }; + const char **pp = s->static_link ? libs + 1 : libs; #else - snprintf(buf, sizeof(buf), "lib%s.so", libraryname); + const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL }; + const char **pp = s->static_link ? libs + 1 : libs; #endif - if (tcc_add_dll(s, buf, 0) == 0) - return 0; - } - - /* 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) + while (*pp) { + if (0 == tcc_add_library_internal(s, *pp, + libraryname, 0, s->library_paths, s->nb_library_paths)) return 0; + ++pp; } return -1; } -int tcc_add_symbol(TCCState *s, const char *name, void *val) +PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname) { - add_elf_sym(symtab_section, (unsigned long)val, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_ABS, name); - return 0; + int ret = tcc_add_library(s, libname); + if (ret < 0) + tcc_error_noabort("library '%s' not found", libname); + return ret; } -int tcc_set_output_type(TCCState *s, int output_type) +/* handle #pragma comment(lib,) */ +ST_FUNC void tcc_add_pragma_libs(TCCState *s1) { - char buf[1024]; - - s->output_type = output_type; - - if (!s->nostdinc) { - /* default include paths */ - /* XXX: reverse order needed if -isystem support */ -#ifndef TCC_TARGET_PE - tcc_add_sysinclude_path(s, CONFIG_SYSROOT "/usr/local/include"); - tcc_add_sysinclude_path(s, CONFIG_SYSROOT "/usr/include"); -#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 + int i; + for (i = 0; i < s1->nb_pragma_libs; i++) + tcc_add_library_err(s1, s1->pragma_libs[i]); +} +LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val) +{ #ifdef TCC_TARGET_PE - snprintf(buf, sizeof(buf), "%s/lib", s->tcc_lib_path); - tcc_add_library_path(s, buf); + /* On x86_64 'val' might not be reachable with a 32bit offset. + So it is handled here as if it were in a DLL. */ + pe_putimport(s, 0, name, (uintptr_t)val); +#else + set_elf_sym(symtab_section, (uintptr_t)val, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_ABS, name); #endif - return 0; } +LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path) +{ + tcc_free(s->tcc_lib_path); + s->tcc_lib_path = tcc_strdup(path); +} + #define WD_ALL 0x0001 /* warning is activated when using -Wall */ #define FD_INVERT 0x0002 /* invert value before storing */ @@ -2173,87 +1196,787 @@ typedef struct FlagDef { 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" }, -}; +static int no_flag(const char **pp) +{ + const char *p = *pp; + if (*p != 'n' || *++p != 'o' || *++p != '-') + return 0; + *pp = p + 1; + return 1; +} -static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags, - const char *name, int value) +ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name) { - int i; + int value, ret; const FlagDef *p; const char *r; + value = 1; 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; + if (no_flag(&r)) + value = 0; + + for (ret = -1, p = flags; p->name; ++p) { + if (ret) { + if (strcmp(r, p->name)) + continue; + } else { + if (0 == (p->flags & WD_ALL)) + continue; + } + if (p->offset) { + *(int*)((char *)s + p->offset) = + p->flags & FD_INVERT ? !value : value; + if (ret) + return 0; + } else { + ret = 0; + } } - return -1; - found: - if (p->flags & FD_INVERT) - value = !value; - *(int *)((uint8_t *)s + p->offset) = value; - return 0; + return ret; } +static int strstart(const char *val, const char **str) +{ + const char *p, *q; + p = *str; + q = val; + while (*q) { + if (*p != *q) + return 0; + p++; + q++; + } + *str = p; + return 1; +} -/* set/reset a warning */ -int tcc_set_warning(TCCState *s, const char *warning_name, int value) +/* 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, const char **ptr) { - int i; - const FlagDef *p; + const char *p, *q; + int ret; - 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; - } + /* there should be 1 or 2 dashes */ + if (*str++ != '-') return 0; - } else { - return set_flag(s, warning_defs, countof(warning_defs), - warning_name, value); + if (*str == '-') + str++; + + /* then str & val should match (potentially up to '=') */ + p = str; + q = val; + + ret = 1; + if (q[0] == '?') { + ++q; + if (no_flag(&p)) + ret = -1; + } + + while (*q != '\0' && *q != '=') { + if (*p != *q) + return 0; + p++; + q++; + } + + /* '=' near eos means ',' or '=' is ok */ + if (*q == '=') { + if (*p == 0) + *ptr = p; + if (*p != ',' && *p != '=') + return 0; + p++; + } else if (*p) { + return 0; + } + *ptr = p; + return ret; +} + +static const char *skip_linker_arg(const char **str) +{ + const char *s1 = *str; + const char *s2 = strchr(s1, ','); + *str = s2 ? s2++ : (s2 = s1 + strlen(s1)); + return s2; +} + +static void copy_linker_arg(char **pp, const char *s, int sep) +{ + const char *q = s; + char *p = *pp; + int l = 0; + if (p && sep) + p[l = strlen(p)] = sep, ++l; + skip_linker_arg(&q); + pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s); +} + +/* set linker options */ +static int tcc_set_linker(TCCState *s, const char *option) +{ + while (*option) { + + const char *p = NULL; + char *end = NULL; + int ignoring = 0; + int ret; + + if (link_option(option, "Bsymbolic", &p)) { + s->symbolic = 1; + } else if (link_option(option, "nostdlib", &p)) { + s->nostdlib = 1; + } else if (link_option(option, "fini=", &p)) { + copy_linker_arg(&s->fini_symbol, p, 0); + ignoring = 1; + } else if (link_option(option, "image-base=", &p) + || link_option(option, "Ttext=", &p)) { + s->text_addr = strtoull(p, &end, 16); + s->has_text_addr = 1; + } else if (link_option(option, "init=", &p)) { + copy_linker_arg(&s->init_symbol, p, 0); + ignoring = 1; + } else if (link_option(option, "oformat=", &p)) { +#if defined(TCC_TARGET_PE) + if (strstart("pe-", &p)) { +#elif PTR_SIZE == 8 + if (strstart("elf64-", &p)) { +#else + if (strstart("elf32-", &p)) { +#endif + s->output_format = TCC_OUTPUT_FORMAT_ELF; + } else if (!strcmp(p, "binary")) { + s->output_format = TCC_OUTPUT_FORMAT_BINARY; +#ifdef TCC_TARGET_COFF + } else if (!strcmp(p, "coff")) { + s->output_format = TCC_OUTPUT_FORMAT_COFF; +#endif + } else + goto err; + + } else if (link_option(option, "as-needed", &p)) { + ignoring = 1; + } else if (link_option(option, "O", &p)) { + ignoring = 1; + } else if (link_option(option, "export-all-symbols", &p)) { + s->rdynamic = 1; + } else if (link_option(option, "rpath=", &p)) { + copy_linker_arg(&s->rpath, p, ':'); + } else if (link_option(option, "enable-new-dtags", &p)) { + s->enable_new_dtags = 1; + } else if (link_option(option, "section-alignment=", &p)) { + s->section_align = strtoul(p, &end, 16); + } else if (link_option(option, "soname=", &p)) { + copy_linker_arg(&s->soname, p, 0); +#ifdef TCC_TARGET_PE + } else if (link_option(option, "large-address-aware", &p)) { + s->pe_characteristics |= 0x20; + } 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") || !strcmp(p, "windows")) { + 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 + goto err; +#endif + } else if (ret = link_option(option, "?whole-archive", &p), ret) { + s->alacarte_link = ret < 0; + } else if (p) { + return 0; + } else { + err: + tcc_error("unsupported linker option '%s'", option); + } + + if (ignoring && s->warn_unsupported) + tcc_warning("unsupported linker option '%s'", option); + + option = skip_linker_arg(&p); } + return 1; } -static const FlagDef flag_defs[] = { +typedef struct TCCOption { + const char *name; + uint16_t index; + uint16_t flags; +} TCCOption; + +enum { + TCC_OPTION_HELP, + TCC_OPTION_HELP2, + TCC_OPTION_v, + TCC_OPTION_I, + TCC_OPTION_D, + TCC_OPTION_U, + TCC_OPTION_P, + TCC_OPTION_L, + TCC_OPTION_B, + TCC_OPTION_l, + TCC_OPTION_bench, + TCC_OPTION_bt, + TCC_OPTION_b, + TCC_OPTION_g, + TCC_OPTION_c, + TCC_OPTION_dumpversion, + TCC_OPTION_d, + TCC_OPTION_static, + TCC_OPTION_std, + TCC_OPTION_shared, + TCC_OPTION_soname, + TCC_OPTION_o, + TCC_OPTION_r, + TCC_OPTION_s, + TCC_OPTION_traditional, + TCC_OPTION_Wl, + TCC_OPTION_Wp, + TCC_OPTION_W, + TCC_OPTION_O, + TCC_OPTION_mfloat_abi, + TCC_OPTION_m, + TCC_OPTION_f, + TCC_OPTION_isystem, + TCC_OPTION_iwithprefix, + TCC_OPTION_include, + TCC_OPTION_nostdinc, + TCC_OPTION_nostdlib, + TCC_OPTION_print_search_dirs, + TCC_OPTION_rdynamic, + TCC_OPTION_param, + TCC_OPTION_pedantic, + TCC_OPTION_pthread, + TCC_OPTION_run, + TCC_OPTION_w, + TCC_OPTION_pipe, + TCC_OPTION_E, + TCC_OPTION_MD, + TCC_OPTION_MF, + TCC_OPTION_x, + TCC_OPTION_ar, + TCC_OPTION_impdef +}; + +#define TCC_OPTION_HAS_ARG 0x0001 +#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ + +static const TCCOption tcc_options[] = { + { "h", TCC_OPTION_HELP, 0 }, + { "-help", TCC_OPTION_HELP, 0 }, + { "?", TCC_OPTION_HELP, 0 }, + { "hh", TCC_OPTION_HELP2, 0 }, + { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG }, + { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG }, + { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG }, + { "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG }, + { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG }, + { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "bench", TCC_OPTION_bench, 0 }, +#ifdef CONFIG_TCC_BACKTRACE + { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG }, +#endif +#ifdef CONFIG_TCC_BCHECK + { "b", TCC_OPTION_b, 0 }, +#endif + { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "c", TCC_OPTION_c, 0 }, + { "dumpversion", TCC_OPTION_dumpversion, 0}, + { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "static", TCC_OPTION_static, 0 }, + { "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "shared", TCC_OPTION_shared, 0 }, + { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG }, + { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG }, + { "-param", TCC_OPTION_param, TCC_OPTION_HAS_ARG }, + { "pedantic", TCC_OPTION_pedantic, 0}, + { "pthread", TCC_OPTION_pthread, 0}, + { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "rdynamic", TCC_OPTION_rdynamic, 0 }, + { "r", TCC_OPTION_r, 0 }, + { "s", TCC_OPTION_s, 0 }, + { "traditional", TCC_OPTION_traditional, 0 }, + { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "Wp,", TCC_OPTION_Wp, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, +#ifdef TCC_TARGET_ARM + { "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG }, +#endif + { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG }, + { "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG }, + { "nostdinc", TCC_OPTION_nostdinc, 0 }, + { "nostdlib", TCC_OPTION_nostdlib, 0 }, + { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, + { "w", TCC_OPTION_w, 0 }, + { "pipe", TCC_OPTION_pipe, 0}, + { "E", TCC_OPTION_E, 0}, + { "MD", TCC_OPTION_MD, 0}, + { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, + { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, + { "ar", TCC_OPTION_ar, 0}, +#ifdef TCC_TARGET_PE + { "impdef", TCC_OPTION_impdef, 0}, +#endif + { NULL, 0, 0 }, +}; + +static const FlagDef options_W[] = { + { 0, 0, "all" }, + { offsetof(TCCState, warn_unsupported), 0, "unsupported" }, + { offsetof(TCCState, warn_write_strings), 0, "write-strings" }, + { offsetof(TCCState, warn_error), 0, "error" }, + { offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" }, + { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL, + "implicit-function-declaration" }, + { 0, 0, NULL } +}; + +static const FlagDef options_f[] = { { 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" }, + { offsetof(TCCState, ms_extensions), 0, "ms-extensions" }, + { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" }, + { 0, 0, NULL } +}; + +static const FlagDef options_m[] = { + { offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" }, +#ifdef TCC_TARGET_X86_64 + { offsetof(TCCState, nosse), FD_INVERT, "sse" }, +#endif + { 0, 0, NULL } }; -/* set/reset a flag */ -int tcc_set_flag(TCCState *s, const char *flag_name, int value) +static void parse_option_D(TCCState *s1, const char *optarg) { - return set_flag(s, flag_defs, countof(flag_defs), - flag_name, value); + char *sym = tcc_strdup(optarg); + char *value = strchr(sym, '='); + if (value) + *value++ = '\0'; + tcc_define_symbol(s1, sym, value); + tcc_free(sym); } -/* set CONFIG_TCCDIR at runtime */ -void tcc_set_lib_path(TCCState *s, const char *path) +static void args_parser_add_file(TCCState *s, const char* filename, int filetype) { - s->tcc_lib_path = tcc_strdup(path); + struct filespec *f = tcc_malloc(sizeof *f + strlen(filename)); + f->type = filetype; + f->alacarte = s->alacarte_link; + strcpy(f->name, filename); + dynarray_add(&s->files, &s->nb_files, f); +} + +static int args_parser_make_argv(const char *r, int *argc, char ***argv) +{ + int ret = 0, q, c; + CString str; + for(;;) { + while (c = (unsigned char)*r, c && c <= ' ') + ++r; + if (c == 0) + break; + q = 0; + cstr_new(&str); + while (c = (unsigned char)*r, c) { + ++r; + if (c == '\\' && (*r == '"' || *r == '\\')) { + c = *r++; + } else if (c == '"') { + q = !q; + continue; + } else if (q == 0 && c <= ' ') { + break; + } + cstr_ccat(&str, c); + } + cstr_ccat(&str, 0); + //printf("<%s>\n", str.data), fflush(stdout); + dynarray_add(argv, argc, tcc_strdup(str.data)); + cstr_free(&str); + ++ret; + } + return ret; +} + +/* read list file */ +static void args_parser_listfile(TCCState *s, + const char *filename, int optind, int *pargc, char ***pargv) +{ + int fd, i; + size_t len; + char *p; + int argc = 0; + char **argv = NULL; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + tcc_error("listfile '%s' not found", filename); + + len = lseek(fd, 0, SEEK_END); + p = tcc_malloc(len + 1), p[len] = 0; + lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd); + + for (i = 0; i < *pargc; ++i) + if (i == optind) + args_parser_make_argv(p, &argc, &argv); + else + dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i])); + + tcc_free(p); + dynarray_reset(&s->argv, &s->argc); + *pargc = s->argc = argc, *pargv = s->argv = argv; } -void tcc_print_stats(TCCState *s, int64_t total_time) +PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind) { - double tt; - tt = (double)total_time / 1000000.0; - if (tt < 0.001) - tt = 0.001; + const TCCOption *popt; + const char *optarg, *r; + const char *run = NULL; + int last_o = -1; + int x; + CString linker_arg; /* collect -Wl options */ + int tool = 0, arg_start = 0, noaction = optind; + char **argv = *pargv; + int argc = *pargc; + + cstr_new(&linker_arg); + + while (optind < argc) { + r = argv[optind]; + if (r[0] == '@' && r[1] != '\0') { + args_parser_listfile(s, r + 1, optind, &argc, &argv); + continue; + } + optind++; + if (tool) { + if (r[0] == '-' && r[1] == 'v' && r[2] == 0) + ++s->verbose; + continue; + } +reparse: + if (r[0] != '-' || r[1] == '\0') { + if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */ + args_parser_add_file(s, r, s->filetype); + if (run) { + tcc_set_options(s, run); + arg_start = optind - 1; + break; + } + continue; + } + + /* find option in table */ + for(popt = tcc_options; ; ++popt) { + const char *p1 = popt->name; + const char *r1 = r + 1; + if (p1 == NULL) + tcc_error("invalid option -- '%s'", r); + if (!strstart(p1, &r1)) + continue; + optarg = r1; + if (popt->flags & TCC_OPTION_HAS_ARG) { + if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) { + if (optind >= argc) + arg_err: + tcc_error("argument to '%s' is missing", r); + optarg = argv[optind++]; + } + } else if (*r1 != '\0') + continue; + break; + } + + switch(popt->index) { + case TCC_OPTION_HELP: + return OPT_HELP; + case TCC_OPTION_HELP2: + return OPT_HELP2; + case TCC_OPTION_I: + tcc_add_include_path(s, optarg); + break; + case TCC_OPTION_D: + parse_option_D(s, optarg); + break; + case TCC_OPTION_U: + tcc_undefine_symbol(s, optarg); + break; + case TCC_OPTION_L: + tcc_add_library_path(s, optarg); + break; + case TCC_OPTION_B: + /* set tcc utilities path (mainly for tcc development) */ + tcc_set_lib_path(s, optarg); + break; + case TCC_OPTION_l: + args_parser_add_file(s, optarg, AFF_TYPE_LIB); + s->nb_libraries++; + break; + case TCC_OPTION_pthread: + parse_option_D(s, "_REENTRANT"); + s->option_pthread = 1; + break; + case TCC_OPTION_bench: + s->do_bench = 1; + break; +#ifdef CONFIG_TCC_BACKTRACE + case TCC_OPTION_bt: + tcc_set_num_callers(atoi(optarg)); + break; +#endif +#ifdef CONFIG_TCC_BCHECK + case TCC_OPTION_b: + s->do_bounds_check = 1; + s->do_debug = 1; + break; +#endif + case TCC_OPTION_g: + s->do_debug = 1; + break; + case TCC_OPTION_c: + x = TCC_OUTPUT_OBJ; + set_output_type: + if (s->output_type) + tcc_warning("-%s: overriding compiler action already specified", popt->name); + s->output_type = x; + break; + case TCC_OPTION_d: + if (*optarg == 'D') + s->dflag = 3; + else if (*optarg == 'M') + s->dflag = 7; + else if (*optarg == 't') + s->dflag = 16; + else if (isnum(*optarg)) + g_debug = atoi(optarg); + else + goto unsupported_option; + break; + case TCC_OPTION_static: + s->static_link = 1; + break; + case TCC_OPTION_std: + /* silently ignore, a current purpose: + allow to use a tcc as a reference compiler for "make test" */ + break; + case TCC_OPTION_shared: + x = TCC_OUTPUT_DLL; + goto set_output_type; + case TCC_OPTION_soname: + s->soname = tcc_strdup(optarg); + break; + case TCC_OPTION_o: + if (s->outfile) { + tcc_warning("multiple -o option"); + tcc_free(s->outfile); + } + s->outfile = tcc_strdup(optarg); + break; + case TCC_OPTION_r: + /* generate a .o merging several output files */ + s->option_r = 1; + x = TCC_OUTPUT_OBJ; + goto set_output_type; + case TCC_OPTION_isystem: + tcc_add_sysinclude_path(s, optarg); + break; + case TCC_OPTION_include: + dynarray_add(&s->cmd_include_files, + &s->nb_cmd_include_files, tcc_strdup(optarg)); + break; + case TCC_OPTION_nostdinc: + s->nostdinc = 1; + break; + case TCC_OPTION_nostdlib: + s->nostdlib = 1; + break; + case TCC_OPTION_run: +#ifndef TCC_IS_NATIVE + tcc_error("-run is not available in a cross compiler"); +#endif + run = optarg; + x = TCC_OUTPUT_MEMORY; + goto set_output_type; + case TCC_OPTION_v: + do ++s->verbose; while (*optarg++ == 'v'); + ++noaction; + break; + case TCC_OPTION_f: + if (set_flag(s, options_f, optarg) < 0) + goto unsupported_option; + break; +#ifdef TCC_TARGET_ARM + case TCC_OPTION_mfloat_abi: + /* tcc doesn't support soft float yet */ + if (!strcmp(optarg, "softfp")) { + s->float_abi = ARM_SOFTFP_FLOAT; + tcc_undefine_symbol(s, "__ARM_PCS_VFP"); + } else if (!strcmp(optarg, "hard")) + s->float_abi = ARM_HARD_FLOAT; + else + tcc_error("unsupported float abi '%s'", optarg); + break; +#endif + case TCC_OPTION_m: + if (set_flag(s, options_m, optarg) < 0) { + if (x = atoi(optarg), x != 32 && x != 64) + goto unsupported_option; + if (PTR_SIZE != x/8) + return x; + ++noaction; + } + break; + case TCC_OPTION_W: + if (set_flag(s, options_W, optarg) < 0) + goto unsupported_option; + break; + case TCC_OPTION_w: + s->warn_none = 1; + break; + case TCC_OPTION_rdynamic: + s->rdynamic = 1; + break; + case TCC_OPTION_Wl: + if (linker_arg.size) + --linker_arg.size, cstr_ccat(&linker_arg, ','); + cstr_cat(&linker_arg, optarg, 0); + if (tcc_set_linker(s, linker_arg.data)) + cstr_free(&linker_arg); + break; + case TCC_OPTION_Wp: + r = optarg; + goto reparse; + case TCC_OPTION_E: + x = TCC_OUTPUT_PREPROCESS; + goto set_output_type; + case TCC_OPTION_P: + s->Pflag = atoi(optarg) + 1; + break; + case TCC_OPTION_MD: + s->gen_deps = 1; + break; + case TCC_OPTION_MF: + s->deps_outfile = tcc_strdup(optarg); + break; + case TCC_OPTION_dumpversion: + printf ("%s\n", TCC_VERSION); + exit(0); + break; + case TCC_OPTION_x: + if (*optarg == 'c') + s->filetype = AFF_TYPE_C; + else if (*optarg == 'a') + s->filetype = AFF_TYPE_ASMPP; + else if (*optarg == 'n') + s->filetype = AFF_TYPE_NONE; + else + tcc_warning("unsupported language '%s'", optarg); + break; + case TCC_OPTION_O: + last_o = atoi(optarg); + break; + case TCC_OPTION_print_search_dirs: + x = OPT_PRINT_DIRS; + goto extra_action; + case TCC_OPTION_impdef: + x = OPT_IMPDEF; + goto extra_action; + case TCC_OPTION_ar: + x = OPT_AR; + extra_action: + arg_start = optind - 1; + if (arg_start != noaction) + tcc_error("cannot parse %s here", r); + tool = x; + break; + case TCC_OPTION_traditional: + case TCC_OPTION_pedantic: + case TCC_OPTION_pipe: + case TCC_OPTION_s: + /* ignored */ + break; + default: +unsupported_option: + if (s->warn_unsupported) + tcc_warning("unsupported option '%s'", r); + break; + } + } + if (last_o > 0) + tcc_define_symbol(s, "__OPTIMIZE__", NULL); + if (linker_arg.size) { + r = linker_arg.data; + goto arg_err; + } + *pargc = argc - arg_start; + *pargv = argv + arg_start; + if (tool) + return tool; + if (optind != noaction) + return 0; + if (s->verbose == 2) + return OPT_PRINT_DIRS; + if (s->verbose) + return OPT_V; + return OPT_HELP; +} + +LIBTCCAPI void tcc_set_options(TCCState *s, const char *r) +{ + char **argv = NULL; + int argc = 0; + args_parser_make_argv(r, &argc, &argv); + tcc_parse_args(s, &argc, &argv, 0); + dynarray_reset(&argv, &argc); +} + +PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time) +{ + if (total_time < 1) + total_time = 1; 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", + fprintf(stderr, "* %d idents, %d lines, %d bytes\n" + "* %0.3f s, %u 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); + (double)total_time/1000, + (unsigned)total_lines*1000/total_time, + (double)total_bytes/1000/total_time); +#ifdef MEM_DEBUG + fprintf(stderr, "* %d bytes memory used\n", mem_max_size); +#endif } |