tinycc/libtcc.c

1663 lines
44 KiB
C
Raw Normal View History

/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tcc.h"
2009-05-12 00:45:56 +08:00
/********************************************************/
/* global variables */
2009-05-06 02:30:13 +08:00
/* use GNU C extensions */
ST_DATA int gnu_ext = 1;
2009-05-06 02:30:13 +08:00
/* use TinyCC extensions */
ST_DATA int tcc_ext = 1;
2009-05-06 02:30:13 +08:00
/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;
2009-05-12 00:45:56 +08:00
/********************************************************/
2009-05-12 00:45:56 +08:00
#ifdef ONE_SOURCE
#include "tccpp.c"
#include "tccgen.c"
#include "tccelf.c"
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
#endif
#ifdef TCC_TARGET_X86_64
#include "x86_64-gen.c"
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#include "i386-asm.c"
#endif
#endif
#ifdef TCC_TARGET_COFF
#include "tcccoff.c"
#endif
#ifdef TCC_TARGET_PE
#include "tccpe.c"
#endif
2011-07-22 17:12:49 +08:00
#endif /* ONE_SOURCE */
/********************************************************/
#ifndef CONFIG_TCC_ASM
ST_FUNC void asm_instr(void)
{
tcc_error("inline asm() not supported");
}
ST_FUNC void asm_global_instr(void)
{
tcc_error("inline asm() not supported");
}
#endif
2009-07-19 04:07:25 +08:00
/********************************************************/
2009-07-19 04:07:25 +08:00
#ifdef _WIN32
static char *normalize_slashes(char *path)
2009-07-19 04:07:25 +08:00
{
char *p;
for (p = path; *p; ++p)
if (*p == '\\')
*p = '/';
return path;
}
static HMODULE tcc_module;
2009-07-19 04:07:25 +08:00
/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static void tcc_set_lib_path_w32(TCCState *s)
2009-07-19 04:07:25 +08:00
{
char path[1024], *p;
GetModuleFileNameA(tcc_module, path, sizeof path);
p = tcc_basename(normalize_slashes(strlwr(path)));
if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5))
p -= 5;
else if (p > path)
p--;
*p = 0;
tcc_set_lib_path(s, path);
}
static void tcc_add_systemdir(TCCState *s)
{
char buf[1000];
GetSystemDirectory(buf, sizeof buf);
tcc_add_library_path(s, normalize_slashes(buf));
}
#ifndef CONFIG_TCC_STATIC
void dlclose(void *p)
{
FreeLibrary((HMODULE)p);
}
#endif
2009-07-19 04:07:25 +08:00
#ifdef LIBTCC_AS_DLL
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
{
if (DLL_PROCESS_ATTACH == dwReason)
tcc_module = hDll;
return TRUE;
}
#endif
#endif
/********************************************************/
/* copy a string and truncate it. */
PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
{
char *q, *q_end;
int c;
if (buf_size > 0) {
q = buf;
q_end = buf + buf_size - 1;
while (q < q_end) {
c = *s++;
if (c == '\0')
break;
*q++ = c;
}
*q = '\0';
}
return buf;
}
/* strcat and truncate. */
PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
PUB_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 */
PUB_FUNC char *tcc_basename(const char *name)
{
char *p = strchr(name, 0);
while (p > name && !IS_DIRSEP(p[-1]))
--p;
return p;
}
/* extract extension part of a file
*
* (if no extension, return pointer to end-of-string)
*/
PUB_FUNC char *tcc_fileextension (const char *name)
{
char *b = tcc_basename(name);
char *e = strrchr(b, '.');
return e ? e : strchr(b, 0);
}
/********************************************************/
/* memory management */
#undef free
#undef malloc
#undef realloc
#ifdef MEM_DEBUG
int mem_cur_size;
int mem_max_size;
unsigned malloc_usable_size(void*);
#endif
PUB_FUNC void tcc_free(void *ptr)
{
#ifdef MEM_DEBUG
mem_cur_size -= malloc_usable_size(ptr);
#endif
free(ptr);
}
PUB_FUNC void *tcc_malloc(unsigned long size)
{
void *ptr;
ptr = malloc(size);
if (!ptr && size)
tcc_error("memory full");
#ifdef MEM_DEBUG
mem_cur_size += malloc_usable_size(ptr);
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
#endif
return ptr;
}
PUB_FUNC void *tcc_mallocz(unsigned long size)
{
void *ptr;
ptr = tcc_malloc(size);
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
void *ptr1;
#ifdef MEM_DEBUG
mem_cur_size -= malloc_usable_size(ptr);
#endif
ptr1 = realloc(ptr, size);
#ifdef MEM_DEBUG
/* NOTE: count not correct if alloc error, but not critical */
mem_cur_size += malloc_usable_size(ptr1);
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
#endif
return ptr1;
}
PUB_FUNC char *tcc_strdup(const char *str)
{
char *ptr;
ptr = tcc_malloc(strlen(str) + 1);
strcpy(ptr, str);
return ptr;
}
PUB_FUNC void tcc_memstats(void)
{
#ifdef MEM_DEBUG
printf("memory in use: %d\n", mem_cur_size);
#endif
}
#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
/********************************************************/
/* dynarrays */
PUB_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data)
{
int nb, nb_alloc;
void **pp;
nb = *nb_ptr;
pp = *ptab;
/* every power of two we double array size */
if ((nb & (nb - 1)) == 0) {
if (!nb)
nb_alloc = 1;
else
nb_alloc = nb * 2;
pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
if (!pp)
tcc_error("memory full");
*ptab = pp;
}
pp[nb++] = data;
*nb_ptr = nb;
}
PUB_FUNC void dynarray_reset(void *pp, int *n)
{
void **p;
for (p = *(void***)pp; *n; ++p, --*n)
if (*p)
tcc_free(*p);
tcc_free(*(void**)pp);
*(void**)pp = NULL;
}
static void tcc_split_path(TCCState *s, void ***p_ary, int *p_nb_ary, const char *in)
{
const char *p;
do {
int c;
CString str;
cstr_new(&str);
for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) {
if (c == '{' && p[1] && p[2] == '}') {
c = p[1], p += 2;
if (c == 'B')
cstr_cat(&str, s->tcc_lib_path);
} else {
cstr_ccat(&str, c);
}
}
cstr_ccat(&str, '\0');
dynarray_add(p_ary, p_nb_ary, str.data);
in = p+1;
} while (*p);
}
/********************************************************/
ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
Section *sec;
sec = tcc_mallocz(sizeof(Section) + strlen(name));
strcpy(sec->name, name);
sec->sh_type = sh_type;
sec->sh_flags = sh_flags;
switch(sh_type) {
case SHT_HASH:
case SHT_REL:
case SHT_RELA:
case SHT_DYNSYM:
case SHT_SYMTAB:
case SHT_DYNAMIC:
sec->sh_addralign = 4;
break;
case SHT_STRTAB:
sec->sh_addralign = 1;
break;
default:
sec->sh_addralign = 32; /* default conservative alignment */
break;
}
if (sh_flags & SHF_PRIVATE) {
dynarray_add((void ***)&s1->priv_sections, &s1->nb_priv_sections, sec);
} else {
sec->sh_num = s1->nb_sections;
dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
}
return sec;
}
static void free_section(Section *s)
{
tcc_free(s->data);
}
/* realloc section and set its content to zero */
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
{
unsigned long size;
unsigned char *data;
size = sec->data_allocated;
if (size == 0)
size = 1;
while (size < new_size)
size = size * 2;
data = tcc_realloc(sec->data, size);
if (!data)
tcc_error("memory full");
memset(data + sec->data_allocated, 0, size - sec->data_allocated);
sec->data = data;
sec->data_allocated = size;
}
/* reserve at least 'size' bytes in section 'sec' from
sec->data_offset. */
ST_FUNC void *section_ptr_add(Section *sec, unsigned long size)
{
unsigned long offset, offset1;
offset = sec->data_offset;
offset1 = offset + size;
if (offset1 > sec->data_allocated)
section_realloc(sec, offset1);
sec->data_offset = offset1;
return sec->data + offset;
}
2010-02-05 03:38:01 +08:00
/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
{
if (size > sec->data_allocated)
section_realloc(sec, size);
if (size > sec->data_offset)
sec->data_offset = size;
}
/* return a reference to a section, and create it if it does not
exists */
ST_FUNC Section *find_section(TCCState *s1, const char *name)
{
Section *sec;
int i;
for(i = 1; i < s1->nb_sections; i++) {
sec = s1->sections[i];
if (!strcmp(name, sec->name))
return sec;
}
/* sections are created as PROGBITS */
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
}
/* update sym->c so that it points to an external symbol in section
'section' with value 'value' */
ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
uplong value, unsigned long size,
int can_add_underscore)
{
int sym_type, sym_bind, sh_num, info, other;
ElfW(Sym) *esym;
const char *name;
char buf1[256];
if (section == NULL)
sh_num = SHN_UNDEF;
else if (section == SECTION_ABS)
sh_num = SHN_ABS;
else
sh_num = section->sh_num;
if ((sym->type.t & VT_BTYPE) == VT_FUNC) {
sym_type = STT_FUNC;
} else if ((sym->type.t & VT_BTYPE) == VT_VOID) {
sym_type = STT_NOTYPE;
} else {
sym_type = STT_OBJECT;
}
if (sym->type.t & VT_STATIC)
sym_bind = STB_LOCAL;
2010-02-28 00:37:59 +08:00
else {
2011-02-01 16:37:53 +08:00
if (sym->type.t & VT_WEAK)
2010-02-28 00:37:59 +08:00
sym_bind = STB_WEAK;
else
sym_bind = STB_GLOBAL;
}
if (!sym->c) {
name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
if (tcc_state->do_bounds_check) {
char buf[32];
/* XXX: avoid doing that for statics ? */
/* if bound checking is activated, we change some function
names by adding the "__bound" prefix */
switch(sym->v) {
#ifdef TCC_TARGET_PE
/* XXX: we rely only on malloc hooks */
case TOK_malloc:
case TOK_free:
case TOK_realloc:
case TOK_memalign:
case TOK_calloc:
#endif
case TOK_memcpy:
case TOK_memmove:
case TOK_memset:
case TOK_strlen:
case TOK_strcpy:
case TOK_alloca:
strcpy(buf, "__bound_");
strcat(buf, name);
name = buf;
break;
}
}
#endif
other = 0;
#ifdef TCC_TARGET_PE
if (sym->type.t & VT_EXPORT)
other |= 1;
if (sym_type == STT_FUNC && sym->type.ref) {
int attr = sym->type.ref->r;
if (FUNC_EXPORT(attr))
other |= 1;
if (FUNC_CALL(attr) == FUNC_STDCALL && can_add_underscore) {
sprintf(buf1, "_%s@%d", name, FUNC_ARGS(attr) * PTR_SIZE);
name = buf1;
other |= 2;
can_add_underscore = 0;
}
} else {
if (find_elf_sym(tcc_state->dynsymtab_section, name))
other |= 4;
if (sym->type.t & VT_IMPORT)
other |= 4;
}
#endif
if (tcc_state->leading_underscore && can_add_underscore) {
buf1[0] = '_';
pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
name = buf1;
}
if (sym->asm_label) {
name = sym->asm_label;
}
info = ELFW(ST_INFO)(sym_bind, sym_type);
sym->c = add_elf_sym(symtab_section, value, size, info, other, sh_num, name);
} else {
esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
esym->st_value = value;
esym->st_size = size;
esym->st_shndx = sh_num;
}
}
ST_FUNC void put_extern_sym(Sym *sym, Section *section,
uplong value, unsigned long size)
{
put_extern_sym2(sym, section, value, size, 1);
}
/* add a new relocation entry to symbol 'sym' in section 's' */
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
{
int c = 0;
if (sym) {
if (0 == sym->c)
put_extern_sym(sym, NULL, 0, 0);
c = sym->c;
}
/* now we can add ELF relocation info */
put_elf_reloc(symtab_section, s, offset, type, c);
}
/********************************************************/
static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
{
int len;
len = strlen(buf);
vsnprintf(buf + len, buf_size - len, fmt, ap);
}
static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
strcat_vprintf(buf, buf_size, fmt, ap);
va_end(ap);
}
static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
{
char buf[2048];
BufferedFile **f;
buf[0] = '\0';
if (file) {
for(f = s1->include_stack; f < s1->include_stack_ptr; f++)
strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
(*f)->filename, (*f)->line_num);
if (file->line_num > 0) {
strcat_printf(buf, sizeof(buf),
"%s:%d: ", file->filename, file->line_num);
} else {
strcat_printf(buf, sizeof(buf),
"%s: ", file->filename);
}
} else {
strcat_printf(buf, sizeof(buf),
"tcc: ");
}
if (is_warning)
strcat_printf(buf, sizeof(buf), "warning: ");
2009-06-17 08:10:56 +08:00
else
strcat_printf(buf, sizeof(buf), "error: ");
strcat_vprintf(buf, sizeof(buf), fmt, ap);
if (!s1->error_func) {
/* default case: stderr */
fprintf(stderr, "%s\n", buf);
} else {
s1->error_func(s1->error_opaque, buf);
}
if (!is_warning || s1->warn_error)
s1->nb_errors++;
}
LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
void (*error_func)(void *opaque, const char *msg))
{
s->error_opaque = error_opaque;
s->error_func = error_func;
}
/* error without aborting current compilation */
PUB_FUNC void tcc_error_noabort(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
}
PUB_FUNC void tcc_error(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
va_start(ap, fmt);
error1(s1, 0, fmt, ap);
va_end(ap);
/* better than nothing: in some cases, we accept to handle errors */
if (s1->error_set_jmp_enabled) {
longjmp(s1->error_jmp_buf, 1);
} else {
/* XXX: eliminate this someday */
exit(1);
}
}
PUB_FUNC void tcc_warning(const char *fmt, ...)
{
TCCState *s1 = tcc_state;
va_list ap;
if (s1->warn_none)
return;
va_start(ap, fmt);
error1(s1, 1, fmt, ap);
va_end(ap);
}
/********************************************************/
/* I/O layer */
ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
BufferedFile *bf;
int buflen = initlen ? initlen : IO_BUF_SIZE;
bf = tcc_malloc(sizeof(BufferedFile) + buflen);
bf->buf_ptr = bf->buffer;
bf->buf_end = bf->buffer + initlen;
bf->buf_end[0] = CH_EOB; /* put eob symbol */
pstrcpy(bf->filename, sizeof(bf->filename), filename);
#ifdef _WIN32
normalize_slashes(bf->filename);
#endif
bf->line_num = 1;
bf->ifndef_macro = 0;
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
bf->fd = -1;
bf->prev = file;
file = bf;
}
ST_FUNC void tcc_close(void)
{
BufferedFile *bf = file;
if (bf->fd > 0) {
close(bf->fd);
total_lines += bf->line_num;
}
file = bf->prev;
tcc_free(bf);
}
ST_FUNC int tcc_open(TCCState *s1, const char *filename)
{
int fd;
if (strcmp(filename, "-") == 0)
fd = 0, filename = "stdin";
else
fd = open(filename, O_RDONLY | O_BINARY);
if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
printf("%s %*s%s\n", fd < 0 ? "nf":"->",
(int)(s1->include_stack_ptr - s1->include_stack), "", filename);
if (fd < 0)
return -1;
tcc_open_bf(s1, filename, 0);
file->fd = fd;
return fd;
}
/* compile the C file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1)
{
Sym *define_start;
SValue *pvtop;
char buf[512];
volatile int section_sym;
#ifdef INC_DEBUG
printf("%s: **** new file\n", file->filename);
#endif
preprocess_init(s1);
cur_text_section = NULL;
funcname = "";
anon_sym = SYM_FIRST_ANOM;
/* file info: full path + filename */
section_sym = 0; /* avoid warning */
if (s1->do_debug) {
section_sym = put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
text_section->sh_num, NULL);
getcwd(buf, sizeof(buf));
#ifdef _WIN32
normalize_slashes(buf);
#endif
pstrcat(buf, sizeof(buf), "/");
put_stabs_r(buf, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
put_stabs_r(file->filename, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
}
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL
symbols can be safely used */
put_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
SHN_ABS, file->filename);
/* define some often used types */
int_type.t = VT_INT;
char_pointer_type.t = VT_BYTE;
mk_pointer(&char_pointer_type);
func_old_type.t = VT_FUNC;
func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
float_type.t = VT_FLOAT;
double_type.t = VT_DOUBLE;
func_float_type.t = VT_FUNC;
func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD);
func_double_type.t = VT_FUNC;
func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD);
#endif
#if 0
/* define 'void *alloca(unsigned int)' builtin function */
{
Sym *s1;
p = anon_sym++;
sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW);
s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
s1->next = NULL;
sym->next = s1;
sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
}
#endif
define_start = define_stack;
nocode_wanted = 1;
if (setjmp(s1->error_jmp_buf) == 0) {
s1->nb_errors = 0;
s1->error_set_jmp_enabled = 1;
ch = file->buf_ptr[0];
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM;
pvtop = vtop;
next();
decl(VT_CONST);
if (tok != TOK_EOF)
expect("declaration");
if (pvtop != vtop)
tcc_warning("internal compiler error: vstack leak? (%d)", vtop - pvtop);
/* end of translation unit info */
if (s1->do_debug) {
put_stabs_r(NULL, N_SO, 0, 0,
text_section->data_offset, text_section, section_sym);
}
}
s1->error_set_jmp_enabled = 0;
/* reset define stack, but leave -Dsymbols (may be incorrect if
they are undefined) */
free_defines(define_start);
gen_inline_functions();
sym_pop(&global_stack, NULL);
sym_pop(&local_stack, NULL);
return s1->nb_errors != 0 ? -1 : 0;
}
LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
int len, ret;
len = strlen(str);
tcc_open_bf(s, "<string>", len);
memcpy(file->buffer, str, len);
ret = tcc_compile(s);
tcc_close();
return ret;
}
/* define a preprocessor symbol. A value can also be provided with the '=' operator */
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
int len1, len2;
/* default value */
if (!value)
value = "1";
len1 = strlen(sym);
len2 = strlen(value);
/* init file structure */
tcc_open_bf(s1, "<define>", len1 + len2 + 1);
memcpy(file->buffer, sym, len1);
file->buffer[len1] = ' ';
memcpy(file->buffer + len1 + 1, value, len2);
/* parse with define parser */
ch = file->buf_ptr[0];
next_nomacro();
parse_define();
tcc_close();
}
/* undefine a preprocessor symbol */
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
TokenSym *ts;
Sym *s;
ts = tok_alloc(sym, strlen(sym));
s = define_find(ts->tok);
/* undefine symbol by putting an invalid name */
if (s)
define_undef(s);
}
static void tcc_cleanup(void)
{
int i, n;
if (NULL == tcc_state)
return;
tcc_state = NULL;
/* free -D defines */
free_defines(NULL);
/* free tokens */
n = tok_ident - TOK_IDENT;
for(i = 0; i < n; i++)
tcc_free(table_ident[i]);
tcc_free(table_ident);
/* free sym_pools */
dynarray_reset(&sym_pools, &nb_sym_pools);
/* string buffer */
cstr_free(&tokcstr);
/* reset symbol stack */
sym_free_first = NULL;
/* cleanup from error/setjmp */
macro_ptr = NULL;
}
LIBTCCAPI TCCState *tcc_new(void)
{
TCCState *s;
2009-05-25 08:07:30 +08:00
char buffer[100];
int a,b,c;
tcc_cleanup();
s = tcc_mallocz(sizeof(TCCState));
if (!s)
return NULL;
tcc_state = s;
2009-07-19 04:07:33 +08:00
#ifdef _WIN32
tcc_set_lib_path_w32(s);
#else
tcc_set_lib_path(s, CONFIG_TCCDIR);
#endif
s->output_type = TCC_OUTPUT_MEMORY;
preprocess_new();
s->include_stack_ptr = s->include_stack;
/* we add dummy defines for some special macros to speed up tests
and to have working defined() */
define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
/* standard defines */
tcc_define_symbol(s, "__STDC__", NULL);
tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
#if defined(TCC_TARGET_I386)
tcc_define_symbol(s, "__i386__", NULL);
tcc_define_symbol(s, "__i386", NULL);
tcc_define_symbol(s, "i386", NULL);
#endif
#if defined(TCC_TARGET_X86_64)
tcc_define_symbol(s, "__x86_64__", NULL);
#endif
#if defined(TCC_TARGET_ARM)
tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
tcc_define_symbol(s, "__arm_elf__", NULL);
tcc_define_symbol(s, "__arm_elf", NULL);
tcc_define_symbol(s, "arm_elf", NULL);
tcc_define_symbol(s, "__arm__", NULL);
tcc_define_symbol(s, "__arm", NULL);
tcc_define_symbol(s, "arm", NULL);
tcc_define_symbol(s, "__APCS_32__", NULL);
#endif
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "_WIN32", NULL);
2009-07-19 04:05:27 +08:00
#ifdef TCC_TARGET_X86_64
tcc_define_symbol(s, "_WIN64", NULL);
#endif
#else
tcc_define_symbol(s, "__unix__", NULL);
tcc_define_symbol(s, "__unix", NULL);
tcc_define_symbol(s, "unix", NULL);
solve tccelf problem on FreeBSD On Sun, Nov 22, 2009 at 05:43:14PM +0100, Luigi Rizzo wrote: > Hi, > there is a well known problem with tcc and FreeBSD in the generation > of elf objects -- see > http://lists.gnu.org/archive/html/tinycc-devel/2005-07/msg00070.html > > Apparently Sergey Lyubka has tried a partial fix to the problem. > I was wondering if Sergey or someone can post some more detail on > what needs to be done so we can try to help fixing this issue I think i have managed to solve the problem and produce almost valid elf files on FreeBSD. The two patches attached address a few problems (trying to explain to the best of my knowledge; i am not very familiar with ELF and the FreeBSD ELF conventions): 1. ELF file format tcc produces an ELF executable which is good for linux but not for FreeBSD. It misses the PHDR section which is almost mandatory for shared executables, puts in the .dynsym section some relocation info that FreeBSD expects to be in .got, and expect the relocation sections to be contiguous. patch-tccelf.c tries to address the above problem using conditional sections (so hopefully can be imported upstream) and also adds the ability to override the name of the dynamic loader through an environment variable (this is important to debug tcc). 2. predefined macros patch-libtcc.c adds/fixes some predefined macros when compiling on FreeBSD: these are __FreeBSD__ and the usual set of __i386__ and __unix__ variants. It also sets __INTEL_COMPILER so we can grab the __aligned macro from cdefs.h , otherwise many programs would fail The resulting elf file is still not 100% correct -- if you strip it, the program will not run (presumably there is some dangling reference). Other than that, program do seem to run correctly. It would be nice to integrate these patches in the main repository. The FreeBSD specific code is in #ifdef so it should not harm linux users cheers luigi
2009-11-29 10:25:29 +08:00
#if defined(__FreeBSD__)
#define str(s) #s
tcc_define_symbol(s, "__FreeBSD__", str( __FreeBSD__));
#undef str
#endif
#if defined(__FreeBSD_kernel__)
tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
#endif
#if defined(__linux)
tcc_define_symbol(s, "__linux__", NULL);
tcc_define_symbol(s, "__linux", NULL);
#endif
#endif
/* tiny C specific defines */
2009-05-25 08:07:30 +08:00
sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
sprintf(buffer, "%d", a*10000 + b*100 + c);
tcc_define_symbol(s, "__TINYC__", buffer);
/* tiny C & gcc defines */
#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
#else
tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
#endif
#ifdef TCC_TARGET_PE
tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
#else
tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
#endif
/* glibc defines */
tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)", "name proto __asm__ (#alias) __THROW");
#ifndef TCC_TARGET_PE
/* default library paths */
tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
/* paths for crt objects */
tcc_split_path(s, (void ***)&s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
#endif
/* no section zero */
dynarray_add((void ***)&s->sections, &s->nb_sections, NULL);
/* create standard sections */
text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
/* symbols are always generated for linking stage */
symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
".strtab",
".hashtab", SHF_PRIVATE);
strtab_section = symtab_section->link;
/* private symbol table for dynamic symbols */
s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
".dynstrtab",
".dynhashtab", SHF_PRIVATE);
s->alacarte_link = 1;
s->nocommon = 1;
#ifdef CHAR_IS_UNSIGNED
s->char_is_unsigned = 1;
#endif
/* enable this if you want symbols with leading underscore on windows: */
#if defined(TCC_TARGET_PE) && 0
s->leading_underscore = 1;
2009-08-27 15:34:35 +08:00
#endif
if (s->section_align == 0)
s->section_align = ELF_PAGE_SIZE;
2009-08-27 15:34:35 +08:00
#ifdef TCC_TARGET_I386
s->seg_size = 32;
#endif
return s;
}
LIBTCCAPI void tcc_delete(TCCState *s1)
{
int i;
tcc_cleanup();
/* free all sections */
for(i = 1; i < s1->nb_sections; i++)
free_section(s1->sections[i]);
dynarray_reset(&s1->sections, &s1->nb_sections);
for(i = 0; i < s1->nb_priv_sections; i++)
free_section(s1->priv_sections[i]);
dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
/* free any loaded DLLs */
for ( i = 0; i < s1->nb_loaded_dlls; i++) {
DLLReference *ref = s1->loaded_dlls[i];
if ( ref->handle )
dlclose(ref->handle);
}
/* free loaded dlls array */
dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
/* free library paths */
dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
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);
tcc: Draft suppoprt for -MD/-MF options In build systems, this is used to automatically collect target dependencies, e.g. ---- 8< (hello.c) ---- #include "hello.h" #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } $ tcc -MD -c hello.c # -> hello.o, hello.d $ cat hello.d hello.o : \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ NOTE: gcc supports -MD only for .c -> .o, but in tcc, we generate dependencies for whatever action is being taken. E.g. for .c -> exe, the result will be: $ tcc -MD -o hello hello.c # -> hello, hello.d hello: \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ /usr/lib/libc.so \ /lib/libc.so.6 \ /usr/lib/ld-linux.so.2 \ /lib/ld-linux.so.2 \ /usr/lib/libc_nonshared.a \ /lib/libc.so.6 \ /usr/lib/libc_nonshared.a \ /home/kirr/local/tcc/lib/tcc/libtcc1.a \ /usr/lib/crtn.o \ So tcc dependency generator is a bit more clever than one used in gcc :) Also, I've updated TODO and Changelog (in not-yet-released section). v2: (Taking inputs from grischka and me myself) - put code to generate deps file into a function. - used tcc_fileextension() instead of open-coding - generate deps only when compilation/preprocessing was successful v3: - use pstrcpy instead of snprintf(buf, sizeof(buf), "%s", ...)
2010-06-21 00:08:12 +08:00
dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
tcc_free(s1->tcc_lib_path);
#ifdef HAVE_SELINUX
munmap (s1->write_mem, s1->mem_size);
munmap (s1->runtime_mem, s1->mem_size);
#else
tcc_free(s1->runtime_mem);
#endif
tcc_free(s1);
}
LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, (void ***)&s->include_paths, &s->nb_include_paths, pathname);
return 0;
}
LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, (void ***)&s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
return 0;
}
ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
const char *ext;
ElfW(Ehdr) ehdr;
2009-07-19 04:07:25 +08:00
int fd, ret, size;
/* find source file type with extension */
ext = tcc_fileextension(filename);
if (ext[0])
ext++;
#ifdef CONFIG_TCC_ASM
/* if .S file, define __ASSEMBLER__ like gcc does */
if (!strcmp(ext, "S"))
tcc_define_symbol(s1, "__ASSEMBLER__", NULL);
#endif
/* open the file */
ret = tcc_open(s1, filename);
if (ret < 0) {
2009-07-19 04:07:25 +08:00
if (flags & AFF_PRINT_ERROR)
tcc_error_noabort("file '%s' not found", filename);
return ret;
}
tcc: Draft suppoprt for -MD/-MF options In build systems, this is used to automatically collect target dependencies, e.g. ---- 8< (hello.c) ---- #include "hello.h" #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } $ tcc -MD -c hello.c # -> hello.o, hello.d $ cat hello.d hello.o : \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ NOTE: gcc supports -MD only for .c -> .o, but in tcc, we generate dependencies for whatever action is being taken. E.g. for .c -> exe, the result will be: $ tcc -MD -o hello hello.c # -> hello, hello.d hello: \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ /usr/lib/libc.so \ /lib/libc.so.6 \ /usr/lib/ld-linux.so.2 \ /lib/ld-linux.so.2 \ /usr/lib/libc_nonshared.a \ /lib/libc.so.6 \ /usr/lib/libc_nonshared.a \ /home/kirr/local/tcc/lib/tcc/libtcc1.a \ /usr/lib/crtn.o \ So tcc dependency generator is a bit more clever than one used in gcc :) Also, I've updated TODO and Changelog (in not-yet-released section). v2: (Taking inputs from grischka and me myself) - put code to generate deps file into a function. - used tcc_fileextension() instead of open-coding - generate deps only when compilation/preprocessing was successful v3: - use pstrcpy instead of snprintf(buf, sizeof(buf), "%s", ...)
2010-06-21 00:08:12 +08:00
/* update target deps */
dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps,
tcc_strdup(filename));
if (flags & AFF_PREPROCESS) {
ret = tcc_preprocess(s1);
2009-07-19 04:07:25 +08:00
goto the_end;
}
if (!ext[0] || !PATHCMP(ext, "c")) {
/* C file assumed */
ret = tcc_compile(s1);
2009-07-19 04:07:25 +08:00
goto the_end;
}
#ifdef CONFIG_TCC_ASM
if (!strcmp(ext, "S")) {
/* preprocessed assembler */
ret = tcc_assemble(s1, 1);
2009-07-19 04:07:25 +08:00
goto the_end;
}
if (!strcmp(ext, "s")) {
/* non preprocessed assembler */
ret = tcc_assemble(s1, 0);
2009-07-19 04:07:25 +08:00
goto the_end;
}
#endif
2009-07-19 04:07:25 +08:00
fd = file->fd;
/* assume executable format: auto guess file type */
size = read(fd, &ehdr, sizeof(ehdr));
lseek(fd, 0, SEEK_SET);
if (size <= 0) {
tcc_error_noabort("could not read header");
2009-07-19 04:07:25 +08:00
goto the_end;
}
if (size == sizeof(ehdr) &&
ehdr.e_ident[0] == ELFMAG0 &&
ehdr.e_ident[1] == ELFMAG1 &&
ehdr.e_ident[2] == ELFMAG2 &&
ehdr.e_ident[3] == ELFMAG3) {
/* do not display line number if error */
file->line_num = 0;
if (ehdr.e_type == ET_REL) {
ret = tcc_load_object_file(s1, fd, 0);
goto the_end;
}
#ifndef TCC_TARGET_PE
if (ehdr.e_type == ET_DYN) {
if (s1->output_type == TCC_OUTPUT_MEMORY) {
void *h;
h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY);
if (h)
ret = 0;
} else {
2009-07-19 04:07:25 +08:00
ret = tcc_load_dll(s1, fd, filename,
(flags & AFF_REFERENCED_DLL) != 0);
}
2009-07-19 04:07:25 +08:00
goto the_end;
}
#endif
tcc_error_noabort("unrecognized ELF file");
2009-07-19 04:07:25 +08:00
goto the_end;
}
if (memcmp((char *)&ehdr, ARMAG, 8) == 0) {
file->line_num = 0; /* do not display line number if error */
ret = tcc_load_archive(s1, fd);
goto the_end;
}
#ifdef TCC_TARGET_COFF
2009-07-19 04:07:25 +08:00
if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) {
ret = tcc_load_coff(s1, fd);
goto the_end;
}
#endif
2009-07-19 04:07:25 +08:00
#ifdef TCC_TARGET_PE
2009-07-19 04:07:25 +08:00
ret = pe_load_file(s1, filename, fd);
#else
/* as GNU ld, consider it is an ld script if not recognized */
ret = tcc_load_ldscript(s1);
#endif
2009-07-19 04:07:25 +08:00
if (ret < 0)
tcc_error_noabort("unrecognized file type");
2009-07-19 04:07:25 +08:00
the_end:
tcc_close();
return ret;
}
LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
{
2009-05-12 00:46:02 +08:00
if (s->output_type == TCC_OUTPUT_PREPROCESS)
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS);
2009-05-12 00:46:02 +08:00
else
return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR);
}
LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, (void ***)&s->library_paths, &s->nb_library_paths, pathname);
return 0;
}
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 < nb_paths; i++) {
snprintf(buf, sizeof(buf), fmt, paths[i], filename);
if (tcc_add_file_internal(s, buf, flags) == 0)
return 0;
}
return -1;
}
#ifndef TCC_TARGET_PE
/* 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)
{
return tcc_add_library_internal(s, "%s/%s", filename, flags,
s->library_paths, s->nb_library_paths);
}
#endif
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)
{
#ifdef 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;
#else
const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
const char **pp = s->static_link ? libs + 1 : libs;
2009-07-19 04:07:25 +08:00
#endif
while (*pp) {
if (0 == tcc_add_library_internal(s, *pp,
libraryname, 0, s->library_paths, s->nb_library_paths))
return 0;
++pp;
}
return -1;
}
2010-01-15 03:56:04 +08:00
LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
{
#ifdef TCC_TARGET_PE
pe_putimport(s, 0, name, val);
#else
add_elf_sym(symtab_section, (uplong)val, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
SHN_ABS, name);
#endif
return 0;
}
LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
{
s->output_type = output_type;
if (!s->nostdinc) {
/* default include paths */
2011-02-02 15:32:53 +08:00
/* -isystem paths have already been handled */
tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
}
/* 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);
}
#ifdef TCC_TARGET_PE
tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
# ifdef _WIN32
tcc_add_systemdir(s);
# endif
#else
/* 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;
}
#define WD_ALL 0x0001 /* warning is activated when using -Wall */
#define FD_INVERT 0x0002 /* invert value before storing */
typedef struct FlagDef {
uint16_t offset;
uint16_t flags;
const char *name;
} FlagDef;
static const FlagDef warning_defs[] = {
{ offsetof(TCCState, warn_unsupported), 0, "unsupported" },
{ offsetof(TCCState, warn_write_strings), 0, "write-strings" },
{ offsetof(TCCState, warn_error), 0, "error" },
{ offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
"implicit-function-declaration" },
};
ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
const char *name, int value)
{
int i;
const FlagDef *p;
const char *r;
r = name;
if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') {
r += 3;
value = !value;
}
for(i = 0, p = flags; i < nb_flags; i++, p++) {
if (!strcmp(r, p->name))
goto found;
}
return -1;
found:
if (p->flags & FD_INVERT)
value = !value;
*(int *)((uint8_t *)s + p->offset) = value;
return 0;
}
2011-10-04 04:36:16 +08:00
/* enable debug */
LIBTCCAPI void tcc_enable_debug(TCCState *s)
{
s->do_debug = 1;
}
/* set/reset a warning */
LIBTCCAPI int tcc_set_warning(TCCState *s, const char *warning_name, int value)
{
int i;
const FlagDef *p;
if (!strcmp(warning_name, "all")) {
for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) {
if (p->flags & WD_ALL)
*(int *)((uint8_t *)s + p->offset) = 1;
}
return 0;
} else {
return set_flag(s, warning_defs, countof(warning_defs),
warning_name, value);
}
}
static const FlagDef flag_defs[] = {
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
{ offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
};
/* set/reset a flag */
PUB_FUNC int tcc_set_flag(TCCState *s, const char *flag_name, int value)
{
return set_flag(s, flag_defs, countof(flag_defs),
flag_name, value);
}
static int strstart(const char *str, const char *val, char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = (char *) p;
return 1;
}
/* Like strstart, but automatically takes into account that ld options can
*
* - start with double or single dash (e.g. '--soname' or '-soname')
* - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
* or '-Wl,-soname=x.so')
*
* you provide `val` always in 'option[=]' form (no leading -)
*/
static int link_option(const char *str, const char *val, char **ptr)
{
const char *p, *q;
/* there should be 1 or 2 dashes */
if (*str++ != '-')
return 0;
if (*str == '-')
str++;
/* then str & val should match (potentialy up to '=') */
p = str;
q = val;
while (*q != '\0' && *q != '=') {
if (*p != *q)
return 0;
p++;
q++;
}
/* '=' near eos means ',' or '=' is ok */
if (*q == '=') {
if (*p != ',' && *p != '=')
return 0;
p++;
q++;
}
if (ptr)
*ptr = (char *) p;
return 1;
}
/* set linker options */
PUB_FUNC const char * tcc_set_linker(TCCState *s, char *option, int multi)
{
char *p = option;
char *end;
while (option && *option) {
end = NULL;
if (link_option(option, "Bsymbolic", &p)) {
s->symbolic = TRUE;
} else if (link_option(option, "fini=", &p)) {
s->fini_symbol = p;
if (s->warn_unsupported)
tcc_warning("ignoring -fini %s", p);
} else if (link_option(option, "image-base=", &p)) {
s->text_addr = strtoull(p, &end, 16);
s->has_text_addr = 1;
} else if (link_option(option, "init=", &p)) {
s->init_symbol = p;
if (s->warn_unsupported)
tcc_warning("ignoring -init %s", p);
} else if (link_option(option, "oformat=", &p)) {
#if defined(TCC_TARGET_PE)
if (strstart(p, "pe-", NULL)) {
#else
#if defined(TCC_TARGET_X86_64)
if (strstart(p, "elf64-", NULL)) {
#else
if (strstart(p, "elf32-", NULL)) {
#endif
#endif
s->output_format = TCC_OUTPUT_FORMAT_ELF;
} else if (!strcmp(p, "binary")) {
s->output_format = TCC_OUTPUT_FORMAT_BINARY;
} else
#ifdef TCC_TARGET_COFF
if (!strcmp(p, "coff")) {
s->output_format = TCC_OUTPUT_FORMAT_COFF;
} else
#endif
{
return p;
}
} else if (link_option(option, "rpath=", &p)) {
s->rpath = p;
} else if (link_option(option, "section-alignment=", &p)) {
s->section_align = strtoul(p, &end, 16);
} else if (link_option(option, "soname=", &p)) {
s->soname = p;
multi = 0;
#ifdef TCC_TARGET_PE
} else if (link_option(option, "file-alignment=", &p)) {
s->pe_file_align = strtoul(p, &end, 16);
} else if (link_option(option, "stack=", &p)) {
s->pe_stack_size = strtoul(p, &end, 10);
} else if (link_option(option, "subsystem=", &p)) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
if (!strcmp(p, "native")) {
s->pe_subsystem = 1;
} else if (!strcmp(p, "console")) {
s->pe_subsystem = 3;
} else if (!strcmp(p, "gui")) {
s->pe_subsystem = 2;
} else if (!strcmp(p, "posix")) {
s->pe_subsystem = 7;
} else if (!strcmp(p, "efiapp")) {
s->pe_subsystem = 10;
} else if (!strcmp(p, "efiboot")) {
s->pe_subsystem = 11;
} else if (!strcmp(p, "efiruntime")) {
s->pe_subsystem = 12;
} else if (!strcmp(p, "efirom")) {
s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
if (!strcmp(p, "wince")) {
s->pe_subsystem = 9;
#endif
} else {
return p;
}
#endif
} else if (link_option(option, "Ttext=", &p)) {
s->text_addr = strtoull(p, &end, 16);
s->has_text_addr = 1;
} else {
return option;
}
if (multi) {
option = NULL;
p = strchr( (end) ? end : p, ',');
if (p) {
*p = 0; /* terminate last option */
option = ++p;
}
} else
option = NULL;
}
return NULL;
}
PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time)
{
double tt;
tt = (double)total_time / 1000000.0;
if (tt < 0.001)
tt = 0.001;
if (total_bytes < 1)
total_bytes = 1;
printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n",
tok_ident - TOK_IDENT, total_lines, total_bytes,
tt, (int)(total_lines / tt),
total_bytes / tt / 1000000.0);
}
/* set CONFIG_TCCDIR at runtime */
LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
{
tcc_free(s->tcc_lib_path);
s->tcc_lib_path = tcc_strdup(path);
}
PUB_FUNC char *tcc_default_target(TCCState *s, const char *default_file)
{
char buf[1024];
char *ext;
const char *name = "a";
if (default_file && strcmp(default_file, "-"))
name = tcc_basename(default_file);
pstrcpy(buf, sizeof(buf), name);
ext = tcc_fileextension(buf);
#ifdef TCC_TARGET_PE
if (s->output_type == TCC_OUTPUT_DLL)
strcpy(ext, ".dll");
else
if (s->output_type == TCC_OUTPUT_EXE)
strcpy(ext, ".exe");
else
#endif
if (( (s->output_type == TCC_OUTPUT_OBJ && !s->reloc_output) ||
(s->output_type == TCC_OUTPUT_PREPROCESS) )
&& *ext)
strcpy(ext, ".o");
else
pstrcpy(buf, sizeof(buf), "a.out");
return tcc_strdup(buf);
}
tcc: Draft suppoprt for -MD/-MF options In build systems, this is used to automatically collect target dependencies, e.g. ---- 8< (hello.c) ---- #include "hello.h" #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } $ tcc -MD -c hello.c # -> hello.o, hello.d $ cat hello.d hello.o : \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ NOTE: gcc supports -MD only for .c -> .o, but in tcc, we generate dependencies for whatever action is being taken. E.g. for .c -> exe, the result will be: $ tcc -MD -o hello hello.c # -> hello, hello.d hello: \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ /usr/lib/libc.so \ /lib/libc.so.6 \ /usr/lib/ld-linux.so.2 \ /lib/ld-linux.so.2 \ /usr/lib/libc_nonshared.a \ /lib/libc.so.6 \ /usr/lib/libc_nonshared.a \ /home/kirr/local/tcc/lib/tcc/libtcc1.a \ /usr/lib/crtn.o \ So tcc dependency generator is a bit more clever than one used in gcc :) Also, I've updated TODO and Changelog (in not-yet-released section). v2: (Taking inputs from grischka and me myself) - put code to generate deps file into a function. - used tcc_fileextension() instead of open-coding - generate deps only when compilation/preprocessing was successful v3: - use pstrcpy instead of snprintf(buf, sizeof(buf), "%s", ...)
2010-06-21 00:08:12 +08:00
PUB_FUNC void tcc_gen_makedeps(TCCState *s, const char *target, const char *filename)
tcc: Draft suppoprt for -MD/-MF options In build systems, this is used to automatically collect target dependencies, e.g. ---- 8< (hello.c) ---- #include "hello.h" #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } $ tcc -MD -c hello.c # -> hello.o, hello.d $ cat hello.d hello.o : \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ NOTE: gcc supports -MD only for .c -> .o, but in tcc, we generate dependencies for whatever action is being taken. E.g. for .c -> exe, the result will be: $ tcc -MD -o hello hello.c # -> hello, hello.d hello: \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ /usr/lib/libc.so \ /lib/libc.so.6 \ /usr/lib/ld-linux.so.2 \ /lib/ld-linux.so.2 \ /usr/lib/libc_nonshared.a \ /lib/libc.so.6 \ /usr/lib/libc_nonshared.a \ /home/kirr/local/tcc/lib/tcc/libtcc1.a \ /usr/lib/crtn.o \ So tcc dependency generator is a bit more clever than one used in gcc :) Also, I've updated TODO and Changelog (in not-yet-released section). v2: (Taking inputs from grischka and me myself) - put code to generate deps file into a function. - used tcc_fileextension() instead of open-coding - generate deps only when compilation/preprocessing was successful v3: - use pstrcpy instead of snprintf(buf, sizeof(buf), "%s", ...)
2010-06-21 00:08:12 +08:00
{
FILE *depout;
char buf[1024], *ext;
int i;
if (!filename) {
/* compute filename automatically
* dir/file.o -> dir/file.d */
pstrcpy(buf, sizeof(buf), target);
ext = tcc_fileextension(buf);
pstrcpy(ext, sizeof(buf) - (ext-buf), ".d");
filename = buf;
}
if (s->verbose)
printf("<- %s\n", filename);
/* XXX return err codes instead of error() ? */
depout = fopen(filename, "w");
if (!depout)
tcc_error("could not open '%s'", filename);
tcc: Draft suppoprt for -MD/-MF options In build systems, this is used to automatically collect target dependencies, e.g. ---- 8< (hello.c) ---- #include "hello.h" #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } $ tcc -MD -c hello.c # -> hello.o, hello.d $ cat hello.d hello.o : \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ NOTE: gcc supports -MD only for .c -> .o, but in tcc, we generate dependencies for whatever action is being taken. E.g. for .c -> exe, the result will be: $ tcc -MD -o hello hello.c # -> hello, hello.d hello: \ /usr/lib/crt1.o \ /usr/lib/crti.o \ hello.c \ hello.h \ /usr/include/stdio.h \ /usr/include/features.h \ /usr/include/bits/predefs.h \ /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs.h \ /usr/include/bits/wordsize.h \ /usr/include/gnu/stubs-32.h \ /home/kirr/local/tcc/lib/tcc/include/stddef.h \ /usr/include/bits/types.h \ /usr/include/bits/wordsize.h \ /usr/include/bits/typesizes.h \ /usr/include/libio.h \ /usr/include/_G_config.h \ /usr/include/wchar.h \ /home/kirr/local/tcc/lib/tcc/include/stdarg.h \ /usr/include/bits/stdio_lim.h \ /usr/include/bits/sys_errlist.h \ /usr/lib/libc.so \ /lib/libc.so.6 \ /usr/lib/ld-linux.so.2 \ /lib/ld-linux.so.2 \ /usr/lib/libc_nonshared.a \ /lib/libc.so.6 \ /usr/lib/libc_nonshared.a \ /home/kirr/local/tcc/lib/tcc/libtcc1.a \ /usr/lib/crtn.o \ So tcc dependency generator is a bit more clever than one used in gcc :) Also, I've updated TODO and Changelog (in not-yet-released section). v2: (Taking inputs from grischka and me myself) - put code to generate deps file into a function. - used tcc_fileextension() instead of open-coding - generate deps only when compilation/preprocessing was successful v3: - use pstrcpy instead of snprintf(buf, sizeof(buf), "%s", ...)
2010-06-21 00:08:12 +08:00
fprintf(depout, "%s : \\\n", target);
for (i=0; i<s->nb_target_deps; ++i)
fprintf(depout, "\t%s \\\n", s->target_deps[i]);
fprintf(depout, "\n");
fclose(depout);
}