tcc-stupidos/libtcc/libtcc.c

689 lines
18 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
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include <stdlib.h>
#include <tcc.h>
#include <tcc/path.h>
#include <tcc/memory.h>
#include <tcc/object.h>
#include "cc/cc.h"
/********************************************************/
/* global variables */
/* XXX: get rid of this ASAP (or maybe not) */
struct TCCState *tcc_state;
/* an array of pointers to memory to be free'd after errors */
void** stk_data;
int nb_stk_data;
/********************************************************/
PUB_FUNC void tcc_enter_state(TCCState *s1)
{
if (s1->error_set_jmp_enabled)
return;
tcc_state = s1;
}
PUB_FUNC void tcc_exit_state(TCCState *s1)
{
if (s1->error_set_jmp_enabled)
return;
tcc_state = NULL;
}
char *
tcc_load_text(int fd)
{
int len;
char *buf;
len = lseek(fd, 0, SEEK_END);
buf = load_data(fd, 0, len + 1); /** XXX: rename func */
if (buf)
{
buf[len] = 0;
}
return (buf);
}
#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 */
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 */
int len;
tcc_enter_state(s1);
s1->error_set_jmp_enabled = 1;
if (setjmp(s1->error_jmp_buf) == 0)
{
s1->nb_errors = 0;
if (fd == -1)
{
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
{
coff_begin_file(s1);
if (filetype & (AFF_TYPE_ASM | AFF_TYPE_ASMPP))
{
tcc_assemble(s1, !!(filetype & AFF_TYPE_ASMPP));
}
else
{
tccgen_compile(s1);
}
coff_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);
}
/* 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 TCC_MEMORY_DEBUG
tcc_memcheck(1);
#endif
#undef gnu_ext
s->gnu_ext = 1;
s->tcc_ext = 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;
#ifdef __CHAR_UNSIGNED__
s->char_is_unsigned = 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 */
coff_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->entryname);
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 TCC_MEMORY_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 */
coff_new(s);
if (output_type == TCC_OUTPUT_OBJ) {
/* always elf for objects */
s->output_format = TCC_OUTPUT_FORMAT_COFF;
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 (!s->nostdlib)
coff_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)
{
COFFFileHeader fhdr;
int obj_type;
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)
{
obj_type = tcc_object_type(fd, &fhdr);
lseek(fd, 0, SEEK_SET);
switch (obj_type)
{
case TCC_BINTYPE_COFF:
ret = coff_load_file(s1, fd, 0);
break;
case TCC_BINTYPE_ARCHIVE:
ret = archive_load(s1, fd, !(flags & AFF_WHOLE_ARCHIVE));
break;
default:
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]);
}
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 */
#ifdef TCC_MEMORY_DEBUG
extern size_t mem_max_size;
#endif /* TCC_MEMORY_DEBUG */
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 TCC_MEMORY_DEBUG
fprintf(stderr, "# memory usage");
fprintf(stderr, " %d max (bytes)\n", mem_max_size);
#endif
}