tcc-stupidos/libtcc/libtcc.c

1012 lines
27 KiB
C

/*
* TCC - Tiny C Compiler
*
* Copyright (c) 2001-2004 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <tcc.h>
#include "utils/string.h"
/********************************************************/
/* global variables */
/* XXX: get rid of this ASAP (or maybe not) */
struct TCCState *tcc_state;
TCC_SEM(static tcc_compile_sem);
/* an array of pointers to memory to be free'd after errors */
void** stk_data;
int nb_stk_data;
/********************************************************/
#ifdef _WIN32
char *normalize_slashes(char *path)
{
char *p;
for (p = path; *p; ++p)
if (*p == '\\')
*p = '/';
return path;
}
#define tcc_module NULL /* NULL means executable itself */
#ifndef CONFIG_TCCDIR
/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static inline char *config_tccdir_w32(char *path)
{
char *p;
GetModuleFileNameA(tcc_module, path, MAX_PATH);
p = tcc_basename(normalize_slashes(strlwr(path)));
if (p > path)
--p;
*p = 0;
return path;
}
#define CONFIG_TCCDIR config_tccdir_w32(alloca(MAX_PATH))
#endif
#endif
/********************************************************/
PUB_FUNC void tcc_enter_state(TCCState *s1)
{
if (s1->error_set_jmp_enabled)
return;
WAIT_SEM(&tcc_compile_sem);
tcc_state = s1;
}
PUB_FUNC void tcc_exit_state(TCCState *s1)
{
if (s1->error_set_jmp_enabled)
return;
tcc_state = NULL;
POST_SEM(&tcc_compile_sem);
}
/* 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);
}
char *tcc_load_text(int fd)
{
int len = lseek(fd, 0, SEEK_END);
char *buf = load_data(fd, 0, len + 1);
if (buf)
buf[len] = 0;
return buf;
}
/********************************************************/
/* memory management */
static void *default_reallocator(void *ptr, unsigned long size)
{
void *ptr1;
if (size == 0) {
free(ptr);
ptr1 = NULL;
}
else {
ptr1 = realloc(ptr, size);
if (!ptr1) {
fprintf(stderr, "memory full\n");
exit (1);
}
}
return ptr1;
}
/* global so that every tcc_alloc()/tcc_free() call doesn't need to be changed */
static void *(*reallocator)(void*, unsigned long) = default_reallocator;
void tcc_set_realloc(TCCReallocFunc *realloc)
{
reallocator = realloc ? realloc : default_reallocator;
}
/* in case MEM_DEBUG is #defined */
#undef tcc_free
#undef tcc_malloc
#undef tcc_realloc
#undef tcc_mallocz
#undef tcc_strdup
PUB_FUNC void tcc_free(void *ptr)
{
reallocator(ptr, 0);
}
PUB_FUNC void *tcc_malloc(unsigned long size)
{
return reallocator(0, size);
}
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
return reallocator(ptr, size);
}
PUB_FUNC void *tcc_mallocz(unsigned long size)
{
void *ptr;
ptr = tcc_malloc(size);
if (size)
memset(ptr, 0, size);
return ptr;
}
PUB_FUNC char *tcc_strdup(const char *str)
{
char *ptr;
ptr = tcc_malloc(strlen(str) + 1);
strcpy(ptr, str);
return ptr;
}
#ifdef MEM_DEBUG
#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;
__attribute__((aligned(16))) unsigned char magic3[4];
};
typedef struct mem_debug_header mem_debug_header_t;
TCC_SEM(static mem_sem);
static mem_debug_header_t *mem_debug_chain;
static unsigned mem_cur_size;
static unsigned mem_max_size;
static int nb_states;
static mem_debug_header_t *malloc_check(void *ptr, const char *msg)
{
mem_debug_header_t * header = MEM_HEADER_PTR(ptr);
if (header->magic1 != MEM_DEBUG_MAGIC1 ||
header->magic2 != MEM_DEBUG_MAGIC2 ||
read32le(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;
}
PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
{
int ofs;
mem_debug_header_t *header;
if (!size)
return NULL;
header = tcc_malloc(sizeof(mem_debug_header_t) + size);
header->magic1 = MEM_DEBUG_MAGIC1;
header->magic2 = MEM_DEBUG_MAGIC2;
header->size = size;
write32le(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;
WAIT_SEM(&mem_sem);
header->next = mem_debug_chain;
header->prev = NULL;
if (header->next)
header->next->prev = header;
mem_debug_chain = header;
mem_cur_size += size;
if (mem_cur_size > mem_max_size)
mem_max_size = mem_cur_size;
POST_SEM(&mem_sem);
return MEM_USER_PTR(header);
}
PUB_FUNC void tcc_free_debug(void *ptr)
{
mem_debug_header_t *header;
if (!ptr)
return;
header = malloc_check(ptr, "tcc_free");
WAIT_SEM(&mem_sem);
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;
POST_SEM(&mem_sem);
tcc_free(header);
}
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
{
void *ptr;
ptr = tcc_malloc_debug(size,file,line);
if (size)
memset(ptr, 0, size);
return ptr;
}
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);
if (!size) {
tcc_free_debug(ptr);
return NULL;
}
header = malloc_check(ptr, "tcc_realloc");
WAIT_SEM(&mem_sem);
mem_cur_size -= header->size;
mem_debug_chain_update = (header == mem_debug_chain);
header = tcc_realloc(header, sizeof(mem_debug_header_t) + size);
header->size = size;
write32le(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;
POST_SEM(&mem_sem);
return MEM_USER_PTR(header);
}
PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
{
char *ptr;
ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
strcpy(ptr, str);
return ptr;
}
PUB_FUNC void tcc_memcheck(int d)
{
WAIT_SEM(&mem_sem);
nb_states += d;
if (0 == nb_states && mem_cur_size) {
mem_debug_header_t *header = mem_debug_chain;
fflush(stdout);
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;
}
fflush(stderr);
mem_cur_size = 0;
mem_max_size = 0;
mem_debug_chain = NULL;
#if MEM_DEBUG-0 == 2
exit(2);
#endif
}
POST_SEM(&mem_sem);
}
/* restore the debug versions */
#define tcc_free(ptr) tcc_free_debug(ptr)
#define tcc_malloc(size) tcc_malloc_debug(size, __FILE__, __LINE__)
#define tcc_mallocz(size) tcc_mallocz_debug(size, __FILE__, __LINE__)
#define tcc_realloc(ptr,size) tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
#define tcc_strdup(str) tcc_strdup_debug(str, __FILE__, __LINE__)
#endif /* MEM_DEBUG */
#ifdef _WIN32
# define realpath(file, buf) _fullpath(buf, file, 260)
#endif
/* for #pragma once */
int normalized_PATHCMP(const char *f1, const char *f2)
{
char *p1, *p2;
int ret = 1;
if (!!(p1 = realpath(f1, NULL))) {
if (!!(p2 = realpath(f2, NULL))) {
ret = PATHCMP(p1, p2);
free(p2); /* realpath() requirement */
}
free(p1);
}
return ret;
}
/********************************************************/
/* dynarrays */
void dynarray_add(void *ptab, int *nb_ptr, void *data)
{
int nb, nb_alloc;
void **pp;
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;
}
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[0]; ++p) {
if (c == '{' && p[1] && p[2] == '}') {
c = p[1], p += 2;
if (c == 'B')
cstr_cat(&str, s->tcc_lib_path, -1);
if (c == 'R')
cstr_cat(&str, CONFIG_SYSROOT, -1);
if (c == 'f' && file) {
/* substitute current file's dir */
const char *f = file->true_filename;
const char *b = tcc_basename(f);
if (b > f)
cstr_cat(&str, f, b - f - 1);
else
cstr_cat(&str, ".", 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);
}
/********************************************************/
/* warning / error */
/* warn_... option bits */
#define WARN_ON 1 /* warning is on (-Woption) */
#define WARN_ERR 2 /* warning is an error (-Werror=option) */
#define WARN_NOE 4 /* warning is not an error (-Wno-error=option) */
/* error1() modes */
enum { ERROR_WARN, ERROR_NOABORT, ERROR_ERROR };
static void error1(int mode, const char *fmt, va_list ap)
{
BufferedFile **pf, *f;
TCCState *s1 = tcc_state;
CString cs;
int line = 0;
tcc_exit_state(s1);
if (mode == ERROR_WARN) {
if (s1->warn_error)
mode = ERROR_ERROR;
if (s1->warn_num) {
/* handle tcc_warning_c(warn_option)(fmt, ...) */
int wopt = *(&s1->warn_none + s1->warn_num);
s1->warn_num = 0;
if (0 == (wopt & WARN_ON))
return;
if (wopt & WARN_ERR)
mode = ERROR_ERROR;
if (wopt & WARN_NOE)
mode = ERROR_WARN;
}
if (s1->warn_none)
return;
}
cstr_new(&cs);
if (fmt[0] == '%' && fmt[1] == 'i' && fmt[2] == ':')
line = va_arg(ap, int), fmt += 3;
f = NULL;
if (s1->error_set_jmp_enabled) { /* we're called while parsing a file */
/* 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++)
cstr_printf(&cs, "In file included from %s:%d:\n",
(*pf)->filename, (*pf)->line_num - 1);
if (0 == line)
line = f->line_num - ((tok_flags & TOK_FLAG_BOL) && !macro_ptr);
cstr_printf(&cs, "%s:%d: ", f->filename, line);
} else if (s1->current_filename) {
cstr_printf(&cs, "%s: ", s1->current_filename);
} else {
cstr_printf(&cs, "tcc: ");
}
cstr_printf(&cs, mode == ERROR_WARN ? "warning: " : "error: ");
if (pp_expr > 1)
pp_error(&cs); /* special handler for preprocessor expression errors */
else
cstr_vprintf(&cs, fmt, ap);
if (!s1->error_func) {
/* default case: stderr */
if (s1 && s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout)
printf("\n"); /* print a newline during tcc -E */
fflush(stdout); /* flush -v output */
fprintf(stderr, "%s\n", (char*)cs.data);
fflush(stderr); /* print error/warning now (win32) */
} else {
s1->error_func(s1->error_opaque, (char*)cs.data);
}
cstr_free(&cs);
if (mode != ERROR_WARN)
s1->nb_errors++;
if (mode == ERROR_ERROR && s1->error_set_jmp_enabled) {
while (nb_stk_data)
tcc_free(*(void**)stk_data[--nb_stk_data]);
longjmp(s1->error_jmp_buf, 1);
}
}
void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc *error_func)
{
s->error_opaque = error_opaque;
s->error_func = error_func;
}
/* error without aborting current compilation */
PUB_FUNC int _tcc_error_noabort(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error1(ERROR_NOABORT, fmt, ap);
va_end(ap);
return -1;
}
#undef _tcc_error
PUB_FUNC void _tcc_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error1(ERROR_ERROR, fmt, ap);
exit(1);
}
#define _tcc_error use_tcc_error_noabort
PUB_FUNC void _tcc_warning(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error1(ERROR_WARN, fmt, ap);
va_end(ap);
}
/********************************************************/
/* I/O layer */
void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
BufferedFile *bf;
int buflen = initlen ? initlen : IO_BUF_SIZE;
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);
#ifdef _WIN32
normalize_slashes(bf->filename);
#endif
bf->true_filename = bf->filename;
bf->line_num = 1;
bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
bf->fd = -1;
bf->prev = file;
bf->prev_tok_flags = tok_flags;
file = bf;
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
}
void tcc_close(void)
{
TCCState *s1 = tcc_state;
BufferedFile *bf = file;
if (bf->fd > 0) {
close(bf->fd);
total_lines += bf->line_num - 1;
}
if (bf->true_filename != bf->filename)
tcc_free(bf->true_filename);
file = bf->prev;
tok_flags = bf->prev_tok_flags;
tcc_free(bf);
}
static 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);
return fd;
}
int tcc_open(TCCState *s1, const char *filename)
{
int fd = _tcc_open(s1, filename);
if (fd < 0)
return -1;
tcc_open_bf(s1, filename, 0);
file->fd = fd;
return 0;
}
/* compile the file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1, int filetype, const char *str, int fd)
{
/* Here we enter the code section where we use the global variables for
parsing and code generation (tccpp.c, tccgen.c, <target>-gen.c).
Other threads need to wait until we're done.
Alternatively we could use thread local storage for those global
variables, which may or may not have advantages */
tcc_enter_state(s1);
s1->error_set_jmp_enabled = 1;
if (setjmp(s1->error_jmp_buf) == 0) {
s1->nb_errors = 0;
if (fd == -1) {
int len = strlen(str);
tcc_open_bf(s1, "<string>", len);
memcpy(file->buffer, str, len);
} else {
tcc_open_bf(s1, str, 0);
file->fd = fd;
}
preprocess_start(s1, filetype);
tccgen_init(s1);
if (s1->output_type == TCC_OUTPUT_PREPROCESS) {
tcc_preprocess(s1);
} else {
tccelf_begin_file(s1);
if (filetype & (AFF_TYPE_ASM | AFF_TYPE_ASMPP)) {
tcc_assemble(s1, !!(filetype & AFF_TYPE_ASMPP));
} else {
tccgen_compile(s1);
}
tccelf_end_file(s1);
}
}
tccgen_finish(s1);
preprocess_end(s1);
s1->error_set_jmp_enabled = 0;
tcc_exit_state(s1);
return s1->nb_errors != 0 ? -1 : 0;
}
int tcc_compile_string(TCCState *s, const char *str)
{
return tcc_compile(s, s->filetype, str, -1);
}
/* define a preprocessor symbol. value can be NULL, sym can be "sym=val" */
void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
const char *eq;
if (NULL == (eq = strchr(sym, '=')))
eq = strchr(sym, 0);
if (NULL == value)
value = *eq ? eq + 1 : "1";
cstr_printf(&s1->cmdline_defs, "#define %.*s %s\n", (int)(eq-sym), sym, value);
}
/* undefine a preprocessor symbol */
void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
cstr_printf(&s1->cmdline_defs, "#undef %s\n", sym);
}
TCCState *tcc_new(void)
{
TCCState *s;
s = tcc_mallocz(sizeof(TCCState));
#ifdef MEM_DEBUG
tcc_memcheck(1);
#endif
#undef gnu_ext
s->gnu_ext = 1;
s->tcc_ext = 1;
s->nocommon = 1;
s->dollars_in_identifiers = 1; /*on by default like in gcc/clang*/
s->cversion = 199901; /* default unless -std=c11 is supplied */
s->warn_implicit_function_declaration = 1;
s->warn_discarded_qualifiers = 1;
s->ms_extensions = 1;
s->unwind_tables = 1;
#ifdef CHAR_IS_UNSIGNED
s->char_is_unsigned = 1;
#endif
s->seg_size = 32;
#ifdef CONFIG_NEW_DTAGS
s->enable_new_dtags = 1;
#endif
s->ppfp = stdout;
/* might be used in error() before preprocess_start() */
s->include_stack_ptr = s->include_stack;
tcc_set_lib_path(s, CONFIG_TCCDIR);
return s;
}
void tcc_delete(TCCState *s1)
{
/* 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->include_paths, &s1->nb_include_paths);
dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
tcc_free(s1->tcc_lib_path);;
tcc_free(s1->rpath);
tcc_free(s1->elf_entryname);
tcc_free(s1->init_symbol);
tcc_free(s1->fini_symbol);
tcc_free(s1->mapfile);
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);
cstr_free(&s1->cmdline_defs);
cstr_free(&s1->cmdline_incl);
cstr_free(&s1->linker_arg);
tcc_free(s1);
#ifdef MEM_DEBUG
tcc_memcheck(-1);
#endif
}
int tcc_set_output_type(TCCState *s, int output_type)
{
s->output_type = output_type;
if (!s->nostdinc) {
/* default include paths */
/* -isystem paths have already been handled */
tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
}
if (output_type == TCC_OUTPUT_PREPROCESS) {
return 0;
}
/* add sections */
tccelf_new(s);
if (output_type == TCC_OUTPUT_OBJ) {
/* always elf for objects */
s->output_format = TCC_OUTPUT_FORMAT_ELF;
return 0;
}
tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
/* paths for crt objects */
tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
if (output_type != TCC_OUTPUT_MEMORY && !s->nostdlib)
tccelf_add_crtbegin(s);
return 0;
}
int tcc_add_include_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
return 0;
}
int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
return 0;
}
static int guess_filetype(const char *filename);
int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
int fd, ret = -1;
if (0 == (flags & AFF_TYPE_MASK))
flags |= guess_filetype(filename);
/* ignore binary files with -E */
if (s1->output_type == TCC_OUTPUT_PREPROCESS
&& (flags & AFF_TYPE_BIN))
return 0;
/* open the file */
fd = _tcc_open(s1, filename);
if (fd < 0) {
if (flags & AFF_PRINT_ERROR)
tcc_error_noabort("file '%s' not found", filename);
return FILE_NOT_FOUND;
}
s1->current_filename = filename;
if (flags & AFF_TYPE_BIN) {
ElfW(Ehdr) ehdr;
int obj_type;
obj_type = tcc_object_type(fd, &ehdr);
lseek(fd, 0, SEEK_SET);
switch (obj_type) {
case AFF_BINTYPE_REL:
ret = tcc_load_object_file(s1, fd, 0);
break;
case AFF_BINTYPE_AR:
ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE));
break;
case AFF_BINTYPE_COFF:
ret = coff_load_file(s1, fd, filename);
break;
default:
/* as GNU ld, consider it is an ld script if not recognized */
ret = tcc_load_ldscript(s1, fd);
if (ret < 0)
tcc_error_noabort("%s: unrecognized file type", filename);
break;
}
close(fd);
} else {
/* update target deps */
dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename));
ret = tcc_compile(s1, flags, filename, fd);
}
s1->current_filename = NULL;
return ret;
}
static int guess_filetype(const char *filename)
{
int filetype = 0;
if (1) {
/* 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, "h")
|| !PATHCMP(ext, "i"))
filetype = AFF_TYPE_C;
else
filetype |= AFF_TYPE_BIN;
} else {
filetype = AFF_TYPE_C;
}
}
return filetype;
}
int tcc_add_file(TCCState *s, const char *filename)
{
return tcc_add_file_internal(s, filename, s->filetype | AFF_PRINT_ERROR);
}
int tcc_add_library_path(TCCState *s, const char *pathname)
{
tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
return 0;
}
static int tcc_add_library_internal(TCCState *s1, const char *fmt,
const char *filename, int flags, char **paths, int nb_paths)
{
char buf[1024];
int i, ret;
for(i = 0; i < nb_paths; i++) {
snprintf(buf, sizeof(buf), fmt, paths[i], filename);
ret = tcc_add_file_internal(s1, buf, flags & ~AFF_PRINT_ERROR);
if (ret != FILE_NOT_FOUND)
return ret;
}
if (flags & AFF_PRINT_ERROR)
tcc_error_noabort("library '%s' not found", filename);
return FILE_NOT_FOUND;
}
/* find and load a dll. Return non zero if not found */
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);
}
/* find [cross-]libtcc1.a and tcc helper objects in library path */
int tcc_add_support(TCCState *s1, const char *filename)
{
char buf[100];
if (CONFIG_TCC_CROSSPREFIX[0])
filename = strcat(strcpy(buf, CONFIG_TCC_CROSSPREFIX), filename);
return tcc_add_dll(s1, filename, AFF_PRINT_ERROR);
}
int tcc_add_crt(TCCState *s1, const char *filename)
{
return tcc_add_library_internal(s1, "%s/%s",
filename, AFF_PRINT_ERROR, s1->crt_paths, s1->nb_crt_paths);
}
/* the library name is the same as the argument of the '-l' option */
int tcc_add_library(TCCState *s, const char *libraryname)
{
static const char * const libs[] = { "%s/lib%s.a", NULL };
const char * const *pp = libs;
int flags = s->filetype & AFF_WHOLE_ARCHIVE;
while (*pp) {
int ret = tcc_add_library_internal(s, *pp,
libraryname, flags, s->library_paths, s->nb_library_paths);
if (ret != FILE_NOT_FOUND)
return ret;
++pp;
}
return tcc_add_dll(s, libraryname, AFF_PRINT_ERROR);
}
/* handle #pragma comment(lib,) */
void tcc_add_pragma_libs(TCCState *s1)
{
int i;
for (i = 0; i < s1->nb_pragma_libs; i++)
tcc_add_library(s1, s1->pragma_libs[i]);
}
int tcc_add_symbol(TCCState *s1, const char *name, const void *val)
{
char buf[256];
if (s1->leading_underscore) {
buf[0] = '_';
pstrcpy(buf + 1, sizeof(buf) - 1, name);
name = buf;
}
set_global_sym(s1, name, NULL, (addr_t)(uintptr_t)val); /* NULL: SHN_ABS */
return 0;
}
void tcc_set_lib_path(TCCState *s, const char *path)
{
tcc_free(s->tcc_lib_path);
s->tcc_lib_path = tcc_strdup(path);
}
/********************************************************/
/* options parser */
PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time)
{
if (!total_time)
total_time = 1;
fprintf(stderr, "# %d idents, %d lines, %u bytes\n"
"# %0.3f s, %u lines/s, %0.1f MB/s\n",
total_idents, total_lines, total_bytes,
(double)total_time/1000,
(unsigned)total_lines*1000/total_time,
(double)total_bytes/1000/total_time);
fprintf(stderr, "# text %u, data.rw %u, data.ro %u, bss %u bytes\n",
s1->total_output[0],
s1->total_output[1],
s1->total_output[2],
s1->total_output[3]
);
#ifdef MEM_DEBUG
fprintf(stderr, "# memory usage");
fprintf(stderr, " %d max (bytes)\n", mem_max_size);
#endif
}