/* * Copyright (c) 2003 - 2007, Nils R. Weller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * nwcc compiler driver */ #include "cc_main.h" #include #include #include #include #include #include #include "driver.h" #include "error.h" #include "defs.h" #include "debug.h" #include "backend.h" #include "misc.h" #include "cfgfile.h" #include "n_libc.h" int Sflag; int sflag; int Eflag; int cflag; int oflag; int gflag; int assembler; int stackprotectflag; int gnuheadersflag = 1; /* XXX */ int pedanticflag; int ansiflag; char *asmflag; char *gnuc_version; int archflag; int abiflag; int abiflag_default; int nostdinc_flag; char *out_file = "a.out"; static void usage(void) { /* XXX add useful stuff here */ puts("You invoked nwcc incorrectly. Please refer to the README file"); puts("for supported command line arguments"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int ch; int i; int ldind = 0; int idx; int nolibnwccflag = 0; size_t ldpos = 0; size_t ldalloc = 0; size_t cppind = 0; char asm_flags[1024]; char *asm_cmdline = NULL; char *ld_flags = NULL; char *ld_args[128]; char *cpp_args[128]; char *ld_cmdline = NULL; char *target_str = NULL; char *abi_str = NULL; struct stat sbuf; struct cfg_option *cfg_file_options; struct ga_option options[] = { { 'D', NULL, 1 }, { 'U', NULL, 1 }, { 'I', NULL, 1 }, { 'l', NULL, 1 }, { 'L', NULL, 1 }, { 'o', NULL, 1 }, { 'n', NULL, 1 }, { 'S', NULL, 0 }, { 'E', NULL, 0 }, { 'c', NULL, 0 }, { 'W', NULL, 0 }, /* ignore */ { 'f', NULL, 0 }, /* ignore */ { 'g', NULL, 0 }, /* ignore */ { 'm', NULL, 0 }, /* ignore */ { 'O', NULL, 0 }, /* ignore */ { 's', NULL, 0 }, /* strip */ { 0, "verboseoffsets", 0 }, { 0, "stackprotect", 0 }, { 0, "nolibnwcc", 0 }, { 0, "pthread", 0 }, /* ignore */ { 0, "print-prog-name", 1 }, { 0, "nostdinc", 0 }, { 0, "rdynamic", 0 }, /* ignore */ { 0, "static", 0 }, /* ignore (this hurts, we need it!) */ { 0, "export-dynamic", 0 }, /* ignore */ { 0, "arch", 1 }, { 0, "gnuc", 1 }, #ifdef __sun { 0, "xarch", 1 }, #endif { 0, "O0", 0 }, /* ignore */ { 0, "O1", 0 }, /* ignore */ { 0, "O2", 0 }, /* ignore */ { 0, "O3", 0 }, /* ignore */ { 0, "ggdb", 0 }, /* ignore */ { 0, "Wall", 0 }, /* ignore */ { 0, "pedantic", 0 }, /* ignore */ { 0, "version", 0 }, { 0, "pg", 0 }, /* ignore */ { 0, "pipe", 0 }, /* ignore */ { 0, "ansi", 0 }, { 0, "std", 0 }, { 0, "mabi", 1 }, { 0, "maix64", 0 }, { 0, "abi", 1 }, { 0, "asm", 1 } }; int nopts = N_OPTIONS(options); if ((cfg_file_options = read_config_files()) != NULL) { /* * There are config files that need to be integrated into * the command line */ char **tmpargv; int tmpargc = argc; tmpargv = make_new_argv(argv, &tmpargc, cfg_file_options, options, nopts); if (tmpargv != NULL) { argv = tmpargv; argc = tmpargc; } } while ((ch = nw_get_arg(argc-1, argv+1, options, nopts, &idx)) != -1) { switch (ch) { case 'n': asm_cmdline = n_xstrdup(n_optarg); break; case 'c': cflag = 1; break; case 's': sflag = 1; break; case 'I': case 'D': case 'U': if (cppind == 126) { (void) fprintf(stderr, "Too many cpp flags\n"); return 1; } if (ch == 'I') { cpp_args[cppind++] = "-I"; } else if (ch == 'D') { cpp_args[cppind++] = "-D"; } else if (ch == 'U') { cpp_args[cppind++] = "-U"; } cpp_args[cppind++] = n_optarg; break; case 'E': Eflag = 1; break; case 'l': case 'L': if (ldind == 126) { (void) fprintf(stderr, "Too many linker flags\n"); return 1; } if (ch == 'l') { ld_args[ldind++] = "-l"; } else if (ch == 'L') { ld_args[ldind++] = "-L"; } ld_args[ldind++] = n_optarg; break; case 'o': out_file = n_xstrdup(n_optarg); oflag = 1; break; case 'S': Sflag = 1; break; case 'O': case 'W': /* Ignore -W/-O */ break; case 'g': gflag = 1; break; case '!': break; case '?': if (idx) { if (strcmp(options[idx].name, "stackprotect") == 0) { stackprotectflag = 1; } else if (strcmp(options[idx].name, "nolibnwcc") == 0) { nolibnwccflag = 1; } else if (strcmp(options[idx].name, "Wall") == 0) { ; /* Ignore */ } else if (options[idx].name[0] == 'O' && isdigit(options[idx].name[1])) { ; /* Ignore */ } else if (strcmp(options[idx].name, "ggdb") == 0) { ; /* Ignore */ } else if (strcmp(options[idx].name, "ansi") == 0) { ansiflag = 1; } else if (strcmp(options[idx].name, "std") == 0) { ; } else if (strcmp(options[idx].name, "pg") == 0) { ; /* Ignore */ } else if (strcmp(options[idx].name, "pedantic") == 0) { pedanticflag = 1; } else if (strcmp(options[idx].name, "pipe") == 0) { ; /* Ignore */ } else if (strcmp(options[idx].name, "pthread") == 0) { ld_args[ldind++] = "-lpthread"; } else if (strcmp(options[idx].name, "rdynamic") == 0) { (void) fprintf(stderr, "Warning: " "-rdynamic is ignored\n"); } else if (strcmp(options[idx].name, "export-dynamic") == 0) { (void) fprintf(stderr, "Warning: " "-export-dynamic is ignored\n"); } else if (strcmp(options[idx].name, "nostdinc") == 0) { nostdinc_flag = 1; } else if (strcmp(options[idx].name, "asm") == 0) { if (asmflag) { (void) fprintf(stderr, "-asm " "used more than once\n"); exit(EXIT_FAILURE); } asmflag = n_xstrdup(n_optarg); } else if (strcmp(options[idx].name, "arch") == 0) { if (target_str != NULL) { (void) fprintf(stderr, "-arch " "used more than once\n"); exit(EXIT_FAILURE); } target_str = n_xstrdup(n_optarg); } else if (strcmp(options[idx].name, "static") == 0) { (void) fprintf(stderr, "Warning: " "ignoring -static option\n"); } else if (strcmp(options[idx].name, "gnuc") == 0) { if (gnuc_version != NULL) { (void) fprintf(stderr, "-gnuc " "used more than once\n"); exit(EXIT_FAILURE); } else if (strlen(n_optarg) > 10) { (void) fprintf(stderr, "Bogus " "argument to -gnuc\n"); exit(EXIT_FAILURE); } gnuc_version = n_xstrdup(n_optarg); } else if (strcmp(options[idx].name, "xarch") == 0) { ; } else if (strcmp(options[idx].name, "print-prog-name") == 0) { char buf[2048]; if (find_cmd(n_optarg, buf, sizeof buf) == -1) { puts(n_optarg); } else { puts(buf); } exit(0); } else if (strcmp(options[idx].name, "maix64") == 0) { abi_str = n_xstrdup("aix64"); /* XXX */ } else if (strcmp(options[idx].name, "mabi") == 0 || strcmp(options[idx].name, "abi") == 0) { if (abi_str != NULL) { (void) fprintf(stderr, "-mabi " "used more than once"); exit(EXIT_FAILURE); } abi_str = n_xstrdup(n_optarg); } else if (strcmp(options[idx].name, "version") == 0) { printf("nwcc %s\n", NWCC_VERSION); printf("Copyright (c) 2003 - 2007 " "Nils Robert Weller\n"); exit(0); } else { usage(); } } else { usage(); } break; default: usage(); } } cpp_args[cppind] = NULL; set_target_arch_and_abi(&archflag, &abiflag, target_str, abi_str); if (target_str != NULL) { int hostarch; int hostabi; get_host_arch(&hostarch, &hostabi); if (archflag != hostarch && !Sflag) { (void) fprintf(stderr, "Warning: Cross-compilation " "implies -S (only generates *.asm file.)\n"); Sflag = 1; } } #if 0 if (sizeof(long) == 8 && archflag == ARCH_POWER) { abiflag = ABI_POWER64; } #endif /* * libnwcc is now linked statically because there isn't * a whole lot of code in it anyway */ if (!nolibnwccflag) { int is_64bit = abiflag == ABI_POWER64 || abiflag == ABI_MIPS_N64 || abiflag == ABI_SPARC64; if (is_64bit) { if (stat(INSTALLDIR "/lib/libnwcc64.o", &sbuf) != -1) { ld_args[ldind++] = INSTALLDIR "/lib/libnwcc64.o"; } else if (stat("./extlibnwcc64.o", &sbuf) != -1) { ld_args[ldind++] = "./extlibnwcc64.o"; } else if (stat(INSTALLDIR "/lib/libnwcc.o", &sbuf) != -1) { ld_args[ldind++] = INSTALLDIR "/lib/libnwcc.o"; } else if (stat(INSTALLDIR "./extlibnwcc.o", &sbuf) != -1) { ld_args[ldind++] = INSTALLDIR "./extlibnwcc.o"; } } else { if (stat(INSTALLDIR "/lib/libnwcc32.o", &sbuf) != -1) { ld_args[ldind++] = INSTALLDIR "/lib/libnwcc32.o"; } else if (stat("./extlibnwcc32.o", &sbuf) != -1) { ld_args[ldind++] = "./extlibnwcc32.o"; } else if (stat(INSTALLDIR "/lib/libnwcc.o", &sbuf) != -1) { ld_args[ldind++] = INSTALLDIR "/lib/libnwcc.o"; } else if (stat("./extlibnwcc.o", &sbuf) != -1) { ld_args[ldind++] = "./extlibnwcc.o"; } } #if 0 if (is_64bit) { if (stat("/usr/local/lib/libnwcc64.o", &sbuf) != -1) { ld_args[ldind++] = "/usr/local/lib/libnwcc64.o"; } else if (stat("./extlibnwcc64.o", &sbuf) != -1) { ld_args[ldind++] = "./extlibnwcc64.o"; } } else { if (stat("/usr/local/lib/libnwcc.o", &sbuf) != -1) { ld_args[ldind++] = "/usr/local/lib/libnwcc.o"; } else if (stat("./extlibnwcc.o", &sbuf) != -1) { ld_args[ldind++] = "./extlibnwcc.o"; } } #endif #ifdef __sgi /* XXX otherwise symbols of libnwcc are unresolved */ ld_args[ldind++] = "-lc"; #endif } if (asmflag == NULL) { asmflag = getenv("NWCC_ASM"); } #if 0 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ asmflag = n_xstrdup("gas"); #endif #ifdef __i386__ # if defined __sun asmflag = n_xstrdup("gas"); assembler = ASM_GAS; strcpy(asm_flags, "--traditional-format -Qy "); # elif defined __FreeBSD__ || defined __OpenBSD__ || defined linux \ || defined __DragonFly__ #if 0 if (asmflag == NULL) { asmflag = n_xstrdup("gas"); } #endif if (asmflag == NULL) { /* * 07/28/07: Finally gas is the default assembler! * This is meant to speed up assembling significantly */ assembler = ASM_GAS /*NASM*/; asmflag = "gas"; *asm_flags = 0; } else if ( strcmp(asmflag, "nasm") == 0 || strcmp(asmflag, "nwasm") == 0 || strcmp(asmflag, "yasm") == 0) { if (strcmp(asmflag, "nwasm") == 0) { assembler = ASM_NWASM; } else { assembler = *asmflag == 'n'? ASM_NASM: ASM_YASM; } strcpy(asm_flags, "-f elf "); if (assembler == ASM_NWASM) { /* * Disable verbose output */ strcat(asm_flags, "-q "); } } else if (strcmp(asmflag, "gas") == 0 || strcmp(asmflag, "as") == 0) { assembler = ASM_GAS; *asm_flags = 0; } else { (void) fprintf(stderr, "Unknown assembler `%s'\n", asmflag); } #elif defined __sun # else # error "Unsupported system (currently only Linux/FreeBSD/OpenBSD on x86 \ are supported)" # endif #elif defined __amd64__ || defined __x86_64__ if (asmflag == NULL) { /* * 07/28/07: Finally gas is the default assembler! * This is meant to speed up assembling significantly */ asmflag = "gas"; assembler = ASM_GAS /*NASM*/; *asm_flags = 0; } else if ( strcmp(asmflag, "yasm") == 0) { assembler = ASM_YASM; /* XXX */ strcpy(asm_flags, "-f elf -m amd64"); } else if (strcmp(asmflag, "gas") == 0 || strcmp(asmflag, "as") == 0) { assembler = ASM_GAS; *asm_flags = 0; } else { (void) fprintf(stderr, "Unknown assembler `%s'\n", asmflag); } #elif defined __sgi assembler = ASM_SGI_AS; /* XXX */ if (abiflag == ABI_MIPS_N64) { strcpy(asm_flags, "-pic2 -elf -EB -O0 -g0 -G0 -w -mips3 " "-64 -t5_ll_sc_bug "); } else { strcpy(asm_flags, "-pic2 -elf -EB -O0 -g0 -G0 -w -mips3 " "-n32 -t5_ll_sc_bug "); } #elif defined __sun || defined __sparc if (abiflag == ABI_SPARC64) { #ifndef linux strcpy(asm_flags, "-xarch=v9 "); #else strcpy(asm_flags, "--64 -Av9a -Qy "); #endif } #elif defined _AIX #if 0 strcpy(asm_flags, "-u -mcom "); #endif if (abiflag == ABI_POWER64) { strcpy(asm_flags, "-u -many -a64 -mppc64"); } else { strcpy(asm_flags, "-u -many"); } #else #error "Unsupported host architecture" #endif if (asm_cmdline != NULL) { strncat(asm_flags, asm_cmdline, sizeof asm_flags-strlen(asm_flags)); } if (cflag && oflag) { /* nwcc -c foo.c -o foo.o */ /* XXX */ sprintf(asm_flags+strlen(asm_flags), " -o %s", out_file); } make_room(&ld_flags, &ldalloc, sizeof "-o" + strlen(out_file)); ldpos += sprintf(ld_flags, "-o %s", out_file); for (i = 0; i < ldind; ++i) { make_room(&ld_flags, &ldalloc, ldpos + strlen(ld_args[i]) + 2); ldpos += sprintf(ld_flags+ldpos, " %s", ld_args[i]); } if (asm_cmdline) { free(asm_cmdline); } if (ld_cmdline) { free(ld_cmdline); } return driver(cpp_args, asm_flags, ld_flags, argv+1); #if 0 - finish n64 on mips (self-compilation?) todo: - -ansi/-pedantic (warn about gnu features) - for () variarble declarations - stupid "c++ class style" scope bug - check the "const int" snippet in log, and the tinycc bugs before that - check const correctnes.s.. char ** vs const char ** - fix initializer array/struct type problems once and for all!!! (see mips initializer output o rwhatever that was) - take a peek at gxemul (groan) - x86 stack allocation bug (update sp before use) - bitfields - diagnostic void<->funcptr - clean up builtins..mips should use x86? that amd64 sysv style stuff should be reused for e.g. sysv on ppc - floating point values in variadic funcs on amd64 #endif }