tcc-stupidos/tcc.c
Thomas Preud'homme 7fb0482a46 Support linker options passed in several -Wl param
ld support arguments to multiple-letter options being passed in two
ways:
* -opt=arg
* -opt arg

libtool generate command line of the second form. This commit add
support for the second form so that libtool works with tcc. The way it
is done is to concatenate all -Wl options into one and then pass it to
set_linker.
2012-03-15 00:25:40 +01:00

622 lines
19 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 ONE_SOURCE
#include "libtcc.c"
#else
#include "tcc.h"
#endif
static char **files;
static int nb_files, nb_libraries;
static int multiple_files;
static int print_search_dirs;
static int output_type;
static int reloc_output;
static char *outfile;
static int do_bench = 0;
static int gen_deps;
static const char *deps_outfile;
static const char *m_option;
#define TCC_OPTION_HAS_ARG 0x0001
#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */
static void help(void)
{
printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001-2006 Fabrice Bellard\n"
"Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n"
" tcc [options...] -run infile [arguments...]\n"
"General options:\n"
" -v display current version, increase verbosity\n"
" -c compile only - generate an object file\n"
" -o outfile set output filename\n"
" -Bdir set tcc internal library and include path\n"
" -bench output compilation statistics\n"
" -run run compiled source\n"
" -fflag set or reset (with 'no-' prefix) 'flag' (see man page)\n"
" -Wwarning set or reset (with 'no-' prefix) 'warning' (see man page)\n"
" -w disable all warnings\n"
"Preprocessor options:\n"
" -E preprocess only\n"
" -Idir add include path 'dir'\n"
" -Dsym[=val] define 'sym' with value 'val'\n"
" -Usym undefine 'sym'\n"
"Linker options:\n"
" -Ldir add library path 'dir'\n"
" -llib link with dynamic or static library 'lib'\n"
" -pthread link with -lpthread and -D_REENTRANT (POSIX Linux)\n"
" -shared generate a shared library\n"
" -soname set name for shared library to be used at runtime\n"
" -static static linking\n"
" -rdynamic export all global symbols to dynamic linker\n"
" -r generate (relocatable) object file\n"
"Debugger options:\n"
" -g generate runtime debug info\n"
#ifdef CONFIG_TCC_BCHECK
" -b compile with built-in memory and bounds checker (implies -g)\n"
#endif
#ifdef CONFIG_TCC_BACKTRACE
" -bt N show N callers in stack traces\n"
#endif
"Misc options:\n"
" -MD generate target dependencies for make\n"
" -MF depfile put generated dependencies here\n"
);
}
typedef struct TCCOption {
const char *name;
uint16_t index;
uint16_t flags;
} TCCOption;
enum {
TCC_OPTION_HELP,
TCC_OPTION_I,
TCC_OPTION_D,
TCC_OPTION_U,
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_static,
TCC_OPTION_shared,
TCC_OPTION_soname,
TCC_OPTION_o,
TCC_OPTION_r,
TCC_OPTION_s,
TCC_OPTION_Wl,
TCC_OPTION_W,
TCC_OPTION_O,
TCC_OPTION_m,
TCC_OPTION_f,
TCC_OPTION_isystem,
TCC_OPTION_nostdinc,
TCC_OPTION_nostdlib,
TCC_OPTION_print_search_dirs,
TCC_OPTION_rdynamic,
TCC_OPTION_pedantic,
TCC_OPTION_pthread,
TCC_OPTION_run,
TCC_OPTION_v,
TCC_OPTION_w,
TCC_OPTION_pipe,
TCC_OPTION_E,
TCC_OPTION_MD,
TCC_OPTION_MF,
TCC_OPTION_x,
};
static const TCCOption tcc_options[] = {
{ "h", TCC_OPTION_HELP, 0 },
{ "-help", TCC_OPTION_HELP, 0 },
{ "?", TCC_OPTION_HELP, 0 },
{ "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
{ "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
{ "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
{ "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 },
{ "static", TCC_OPTION_static, 0 },
{ "shared", TCC_OPTION_shared, 0 },
{ "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
{ "o", TCC_OPTION_o, 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 },
{ "Wl,", TCC_OPTION_Wl, 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 },
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG },
{ "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
{ "nostdinc", TCC_OPTION_nostdinc, 0 },
{ "nostdlib", TCC_OPTION_nostdlib, 0 },
{ "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
{ "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
{ "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 },
{ NULL },
};
static int64_t getclock_us(void)
{
#ifdef _WIN32
struct _timeb tb;
_ftime(&tb);
return (tb.time * 1000LL + tb.millitm) * 1000LL;
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000LL + tv.tv_usec;
#endif
}
/* convert 'str' into an array of space separated strings */
static int expand_args(char ***pargv, const char *str)
{
const char *s1;
char **argv, *arg;
int argc, len;
argc = 0;
argv = NULL;
for(;;) {
while (is_space(*str))
str++;
if (*str == '\0')
break;
s1 = str;
while (*str != '\0' && !is_space(*str))
str++;
len = str - s1;
arg = tcc_malloc(len + 1);
memcpy(arg, s1, len);
arg[len] = '\0';
dynarray_add((void ***)&argv, &argc, arg);
}
*pargv = argv;
return argc;
}
/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#ifdef _WIN32
#include <process.h>
static int execvp_win32(const char *prog, char **argv)
{
int ret = spawnvp(P_NOWAIT, prog, (char const*const*)argv);
if (-1 == ret)
return ret;
cwait(&ret, ret, WAIT_CHILD);
exit(ret);
}
#define execvp execvp_win32
#endif
static void exec_other_tcc(TCCState *s, char **argv, const char *optarg)
{
char child_path[4096], *child_name; const char *target;
switch (atoi(optarg)) {
#ifdef TCC_TARGET_I386
case 32: break;
case 64: target = "x86_64";
#else
case 64: break;
case 32: target = "i386";
#endif
pstrcpy(child_path, sizeof child_path - 40, argv[0]);
child_name = tcc_basename(child_path);
strcpy(child_name, target);
#ifdef TCC_TARGET_PE
strcat(child_name, "-win32");
#endif
strcat(child_name, "-tcc");
if (strcmp(argv[0], child_path)) {
if (s->verbose > 0)
printf("tcc: using '%s'\n", child_name), fflush(stdout);
execvp(argv[0] = child_path, argv);
}
tcc_error("'%s' not found", child_name);
case 0: /* ignore -march etc. */
break;
default:
tcc_warning("unsupported option \"-m%s\"", optarg);
}
}
#endif
static void parse_option_D(TCCState *s1, const char *optarg)
{
char *sym = tcc_strdup(optarg);
char *value = strchr(sym, '=');
if (value)
*value++ = '\0';
tcc_define_symbol(s1, sym, value);
tcc_free(sym);
}
static int parse_args(TCCState *s, int argc, char **argv)
{
int optind;
const TCCOption *popt;
const char *optarg, *p1, *r1;
char *r;
int was_pthread;
char *linker_arg = NULL;
unsigned long linker_argsize = 0;
was_pthread = 0; /* is set if commandline contains -pthread key */
optind = 1;
while (optind < argc) {
r = argv[optind++];
if (r[0] != '-' || r[1] == '\0') {
/* add a new file */
dynarray_add((void ***)&files, &nb_files, r);
if (!multiple_files) {
optind--;
/* argv[0] will be this file */
break;
}
} else {
/* find option in table (match only the first chars */
popt = tcc_options;
for(;;) {
p1 = popt->name;
if (p1 == NULL)
tcc_error("invalid option -- '%s'", r);
r1 = r + 1;
for(;;) {
if (*p1 == '\0')
goto option_found;
if (*r1 != *p1)
break;
p1++;
r1++;
}
popt++;
}
option_found:
if (popt->flags & TCC_OPTION_HAS_ARG) {
if (*r1 != '\0' || (popt->flags & TCC_OPTION_NOSEP)) {
optarg = r1;
} else {
if (optind >= argc)
tcc_error("argument to '%s' is missing", r);
optarg = argv[optind++];
}
} else {
if (*r1 != '\0')
return 0;
optarg = NULL;
}
switch(popt->index) {
case TCC_OPTION_HELP:
return 0;
case TCC_OPTION_I:
if (tcc_add_include_path(s, optarg) < 0)
tcc_error("too many include paths");
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:
dynarray_add((void ***)&files, &nb_files, r);
nb_libraries++;
break;
case TCC_OPTION_pthread:
was_pthread = 1;
parse_option_D(s, "_REENTRANT");
break;
case TCC_OPTION_bench:
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:
multiple_files = 1;
output_type = TCC_OUTPUT_OBJ;
break;
case TCC_OPTION_static:
s->static_link = 1;
break;
case TCC_OPTION_shared:
output_type = TCC_OUTPUT_DLL;
break;
case TCC_OPTION_soname:
s->soname = optarg;
break;
case TCC_OPTION_m:
m_option = optarg;
break;
case TCC_OPTION_o:
multiple_files = 1;
outfile = tcc_strdup(optarg);
break;
case TCC_OPTION_r:
/* generate a .o merging several output files */
reloc_output = 1;
output_type = TCC_OUTPUT_OBJ;
break;
case TCC_OPTION_isystem:
tcc_add_sysinclude_path(s, optarg);
break;
case TCC_OPTION_nostdinc:
s->nostdinc = 1;
break;
case TCC_OPTION_nostdlib:
s->nostdlib = 1;
break;
case TCC_OPTION_print_search_dirs:
print_search_dirs = 1;
break;
case TCC_OPTION_run:
{
int argc1;
char **argv1;
argc1 = expand_args(&argv1, optarg);
if (argc1 > 0) {
parse_args(s, argc1, argv1);
}
multiple_files = 0;
output_type = TCC_OUTPUT_MEMORY;
break;
}
case TCC_OPTION_v:
do ++s->verbose; while (*optarg++ == 'v');
break;
case TCC_OPTION_f:
if (tcc_set_flag(s, optarg, 1) < 0 && s->warn_unsupported)
goto unsupported_option;
break;
case TCC_OPTION_W:
if (tcc_set_warning(s, optarg, 1) < 0 &&
s->warn_unsupported)
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) {
linker_argsize = strlen(optarg) + 1;
linker_arg = tcc_malloc(linker_argsize);
pstrcpy(linker_arg, linker_argsize, optarg);
}
else {
linker_argsize += strlen(optarg) + 1;
linker_arg = tcc_realloc(linker_arg, linker_argsize);
pstrcat(linker_arg, linker_argsize, ",");
pstrcat(linker_arg, linker_argsize, optarg);
}
break;
case TCC_OPTION_E:
output_type = TCC_OUTPUT_PREPROCESS;
break;
case TCC_OPTION_MD:
gen_deps = 1;
break;
case TCC_OPTION_MF:
deps_outfile = optarg;
break;
case TCC_OPTION_x:
break;
default:
if (s->warn_unsupported) {
unsupported_option:
tcc_warning("unsupported option '%s'", r);
}
break;
}
}
}
if ((r = (char *) tcc_set_linker(s, (char *)linker_arg, TRUE)))
tcc_error("unsupported linker option '%s'", r);
tcc_free(linker_arg);
/* fixme: these options could be different on your platform */
if (was_pthread && output_type != TCC_OUTPUT_OBJ) {
dynarray_add((void ***)&files, &nb_files, "-lpthread");
nb_libraries++;
}
return optind;
}
int main(int argc, char **argv)
{
int i;
TCCState *s;
int nb_objfiles, ret, optind;
int64_t start_time = 0;
const char *default_file = NULL;
s = tcc_new();
output_type = TCC_OUTPUT_EXE;
outfile = NULL;
multiple_files = 1;
files = NULL;
nb_files = 0;
nb_libraries = 0;
reloc_output = 0;
print_search_dirs = 0;
m_option = NULL;
ret = 0;
optind = parse_args(s, argc, argv);
#if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386
if (m_option)
exec_other_tcc(s, argv, m_option);
#endif
if (print_search_dirs) {
/* enough for Linux kernel */
printf("install: %s/\n", s->tcc_lib_path);
return 0;
}
if (s->verbose)
printf("tcc version %s\n", TCC_VERSION);
if (optind == 0 || nb_files == 0) {
if (optind && s->verbose)
return 0;
help();
return 1;
}
nb_objfiles = nb_files - nb_libraries;
/* if outfile provided without other options, we output an
executable */
if (outfile && output_type == TCC_OUTPUT_MEMORY)
output_type = TCC_OUTPUT_EXE;
/* check -c consistency : only single file handled. XXX: checks file type */
if (output_type == TCC_OUTPUT_OBJ && !reloc_output) {
/* accepts only a single input file */
if (nb_objfiles != 1)
tcc_error("cannot specify multiple files with -c");
if (nb_libraries != 0)
tcc_error("cannot specify libraries with -c");
}
if (output_type == TCC_OUTPUT_PREPROCESS) {
if (!outfile) {
s->outfile = stdout;
} else {
s->outfile = fopen(outfile, "w");
if (!s->outfile)
tcc_error("could not open '%s'", outfile);
}
}
if (do_bench) {
start_time = getclock_us();
}
tcc_set_output_type(s, output_type);
s->reloc_output = reloc_output;
/* compile or add each files or library */
for(i = 0; i < nb_files && ret == 0; i++) {
const char *filename;
filename = files[i];
if (filename[0] == '-' && filename[1] == 'l') {
if (tcc_add_library(s, filename + 2) < 0) {
tcc_error_noabort("cannot find %s", filename);
ret = 1;
}
} else {
if (1 == s->verbose)
printf("-> %s\n", filename);
if (tcc_add_file(s, filename) < 0)
ret = 1;
if (!default_file)
default_file = filename;
}
}
/* free all files */
tcc_free(files);
if (0 == ret) {
if (do_bench)
tcc_print_stats(s, getclock_us() - start_time);
if (s->output_type == TCC_OUTPUT_MEMORY) {
#ifdef TCC_IS_NATIVE
ret = tcc_run(s, argc - optind, argv + optind);
#else
tcc_error_noabort("-run is not available in a cross compiler");
#endif
} else if (s->output_type == TCC_OUTPUT_PREPROCESS) {
if (s->outfile)
fclose(s->outfile);
} else {
if (!outfile)
outfile = tcc_default_target(s, default_file);
ret = !!tcc_output_file(s, outfile);
/* dump collected dependencies */
if (gen_deps && !ret)
tcc_gen_makedeps(s, outfile, deps_outfile);
}
}
tcc_delete(s);
tcc_free(outfile);
#ifdef MEM_DEBUG
if (do_bench) {
printf("memory: %d bytes, max = %d bytes\n", mem_cur_size, mem_max_size);
}
#endif
return ret;
}