Home | History | Annotate | Download | only in bcc
      1 /*
      2  * Copyright 2010-2012, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <ctype.h>
     18 #include <dlfcn.h>
     19 #include <stdint.h>
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 
     24 #include <errno.h>
     25 #include <sys/stat.h>
     26 #include <sys/types.h>
     27 
     28 #if defined(__HOST__)
     29   #if defined(__cplusplus)
     30     extern "C" {
     31   #endif
     32       extern char *TARGET_TRIPLE_STRING;
     33   #if defined(__cplusplus)
     34     };
     35   #endif
     36 #else
     37 #endif
     38 
     39 #include <bcc/bcc.h>
     40 
     41 #define DEFAULT_OUTPUT_FILENAME "a.out"
     42 
     43 typedef int (*RootPtr)();
     44 
     45 // This is a separate function so it can easily be set by breakpoint in gdb.
     46 static int run(RootPtr rootFunc) {
     47   return rootFunc();
     48 }
     49 
     50 enum OutputType {
     51   OT_Executable,
     52   OT_Relocatable,
     53   OT_SharedObject
     54 };
     55 
     56 enum OutputType OutType = OT_Executable;
     57 enum bccRelocModelEnum OutRelocModel = bccRelocDefault;
     58 const char *InFile = NULL;
     59 const char *OutFile = NULL;
     60 const char *IntermediateOutFile = NULL;
     61 bool RunRoot = false;
     62 
     63 struct OptionInfo {
     64   const char *option_name;
     65 
     66   // min_option_argc is the minimum number of arguments this option should
     67   // have. This is for sanity check before invoking processing function.
     68   unsigned min_option_argc;
     69 
     70   const char *argument_desc;
     71   const char *help_message;
     72 
     73   // The function to process this option. Return the number of arguments it
     74   // consumed or < 0 if there's an error during the processing.
     75   int (*process)(int argc, char **arg);
     76 };
     77 
     78 // forward declaration of option processing functions
     79 static int optSetTriple(int, char **);
     80 static int optSetInput(int, char **);
     81 static int optSetOutput(int, char **);
     82 static int optSetIntermediateOutput(int, char **);
     83 static int optOutputReloc(int, char **);
     84 static int optSetOutputPIC(int, char **);
     85 static int optSetOutputShared(int, char **);
     86 static int optRunRoot(int, char **);
     87 static int optHelp(int, char **);
     88 
     89 static const struct OptionInfo Options[] = {
     90 #if defined(__HOST__)
     91   { "C", 1, "triple", "set the triple string.",
     92     optSetTriple },
     93 #endif
     94 
     95   { "c", 0, NULL, "compile and assemble, but do not link.",
     96     optOutputReloc },
     97 
     98   { "fPIC", 0, NULL,  "Generate position-independent code, if possible.",
     99     optSetOutputPIC },
    100 
    101   { "o", 1, "output", "write the native result to an output file.",
    102     optSetOutput },
    103 
    104   // FIXME: this option will be removed in the future when MCLinker is capable
    105   //        of generating shared library directly from given bitcode. It only
    106   //        takes effect when -shared is supplied.
    107   { "or", 1, NULL, "set the output filename for the intermediate relocatable.",
    108     optSetIntermediateOutput },
    109 
    110 
    111   { "shared", 0, NULL, "create a shared library.",
    112     optSetOutputShared },
    113 
    114   { "R", 0, NULL, "run root() method after a successful load and compile.",
    115     optRunRoot },
    116 
    117   { "h", 0, NULL, "print this help.",
    118     optHelp },
    119 };
    120 #define NUM_OPTIONS (sizeof(Options) / sizeof(struct OptionInfo))
    121 
    122 static int parseOption(int argc, char** argv) {
    123   if (argc <= 1) {
    124     optHelp(argc, argv);
    125     return 0; // unreachable
    126   }
    127 
    128   // argv[i] is the current processing argument from command line
    129   int i = 1;
    130   while (i < argc) {
    131     const unsigned left_argc = argc - i - 1;
    132 
    133     if (argv[i][0] == '-') {
    134       // Find the corresponding OptionInfo object
    135       unsigned opt_idx = 0;
    136       while (opt_idx < NUM_OPTIONS) {
    137         if (::strcmp(&argv[i][1], Options[opt_idx].option_name) == 0) {
    138           const struct OptionInfo *cur_option = &Options[opt_idx];
    139           if (left_argc < cur_option->min_option_argc) {
    140             fprintf(stderr, "%s: '%s' requires at least %u arguments", argv[0],
    141                     cur_option->option_name, cur_option->min_option_argc);
    142             return 1;
    143           }
    144 
    145           int result = cur_option->process(left_argc, &argv[i]);
    146           if (result >= 0) {
    147             // consume the used arguments
    148             i += result;
    149           } else {
    150             // error occurs
    151             return 1;
    152           }
    153 
    154           break;
    155         }
    156         ++opt_idx;
    157       }
    158       if (opt_idx >= NUM_OPTIONS) {
    159         fprintf(stderr, "%s: unrecognized option '%s'", argv[0], argv[i]);
    160         return 1;
    161       }
    162     } else {
    163       if (InFile == NULL) {
    164         optSetInput(left_argc, &argv[i]);
    165       } else {
    166         fprintf(stderr, "%s: only a single input file is allowed currently.",
    167                 argv[0]);
    168         return 1;
    169       }
    170     }
    171     i++;
    172   }
    173 
    174   return 0;
    175 }
    176 
    177 static BCCScriptRef loadScript() {
    178   if (!InFile) {
    179     fprintf(stderr, "input file required.\n");
    180     return NULL;
    181   }
    182 
    183   BCCScriptRef script = bccCreateScript();
    184 
    185   if (bccReadFile(script, InFile, /* flags */BCC_SKIP_DEP_SHA1) != 0) {
    186     fprintf(stderr, "bcc: FAILS to read bitcode.");
    187     bccDisposeScript(script);
    188     return NULL;
    189   }
    190 
    191   char *output = NULL;
    192 
    193   if (OutFile != NULL) {
    194     // Copy the outFile since we're going to modify it
    195     size_t outFileLen = strlen(OutFile);
    196     output = new char [outFileLen + 1];
    197     strncpy(output, OutFile, outFileLen);
    198   } else {
    199     if (OutType == OT_Executable) {
    200       output = new char [(sizeof(DEFAULT_OUTPUT_FILENAME) - 1) + 1];
    201       strncpy(output, DEFAULT_OUTPUT_FILENAME,
    202                   sizeof(DEFAULT_OUTPUT_FILENAME) - 1);
    203     } else {
    204       size_t inFileLen = strlen(InFile);
    205       output = new char [inFileLen + 3 /* ensure there's room for .so */ + 1];
    206       strncpy(output, InFile, inFileLen);
    207 
    208       char *fileExtension = strrchr(output, '.');
    209       if (fileExtension == NULL) {
    210         // append suffix
    211         fileExtension = output + inFileLen;
    212         *fileExtension = '.';
    213       }
    214 
    215       fileExtension++;  // skip '.'
    216       if (OutType == OT_Relocatable) {
    217         *fileExtension++ = 'o';
    218       } else /* must be OT_SharedObject */{
    219         *fileExtension++ = 's';
    220         *fileExtension++ = 'o';
    221       }
    222       *fileExtension++ = '\0';
    223     }
    224   }
    225 
    226   int bccResult = 0;
    227   const char *errMsg = NULL;
    228   switch (OutType) {
    229     case OT_Executable: {
    230       bccResult = 1;
    231       errMsg = "generation of executable is unsupported currently.";
    232       break;
    233     }
    234     case OT_Relocatable: {
    235       bccResult = bccPrepareRelocatable(script, output, OutRelocModel, 0);
    236       errMsg = "failed to generate relocatable.";
    237       break;
    238     }
    239     case OT_SharedObject: {
    240       if (IntermediateOutFile != NULL) {
    241         bccResult =
    242             bccPrepareRelocatable(script, IntermediateOutFile, bccRelocPIC, 0);
    243         errMsg = "failed to generate intermediate relocatable.";
    244       }
    245 
    246       if (bccResult == 0) {
    247         bccResult =
    248             bccPrepareSharedObject(script, IntermediateOutFile, output, 0);
    249         errMsg = "failed to generate shared library.";
    250       }
    251       break;
    252     }
    253   }
    254 
    255   delete [] output;
    256 
    257   if (bccResult == 0) {
    258     return script;
    259   } else {
    260     fprintf(stderr, "bcc: %s\n", errMsg);
    261     bccDisposeScript(script);
    262     return NULL;
    263   }
    264 }
    265 
    266 static int runRoot(BCCScriptRef script) {
    267   RootPtr rootPointer =
    268       reinterpret_cast<RootPtr>(bccGetFuncAddr(script, "main"));
    269 
    270   if (!rootPointer) {
    271     rootPointer = reinterpret_cast<RootPtr>(bccGetFuncAddr(script, "root"));
    272   }
    273   if (!rootPointer) {
    274     rootPointer = reinterpret_cast<RootPtr>(bccGetFuncAddr(script, "_Z4rootv"));
    275   }
    276   if (!rootPointer) {
    277     fprintf(stderr, "Could not find root or main or mangled root.\n");
    278     return 1;
    279   }
    280 
    281   fprintf(stderr, "Executing compiled code:\n");
    282 
    283   int result = run(rootPointer);
    284   fprintf(stderr, "result: %d\n", result);
    285 
    286   return 0;
    287 }
    288 
    289 int main(int argc, char** argv) {
    290   if(parseOption(argc, argv)) {
    291     return 1;
    292   }
    293 
    294   BCCScriptRef script;
    295 
    296   if((script = loadScript()) == NULL) {
    297     return 2;
    298   }
    299 
    300   if(RunRoot && runRoot(script)) {
    301     return 6;
    302   }
    303 
    304   bccDisposeScript(script);
    305 
    306   return 0;
    307 }
    308 
    309 /*
    310  * Functions to process the command line option.
    311  */
    312 #if defined(__HOST__)
    313 static int optSetTriple(int, char **arg) {
    314   TARGET_TRIPLE_STRING = arg[1];
    315   return 1;
    316 }
    317 #endif
    318 
    319 static int optSetInput(int, char **arg) {
    320   // Check the input file path
    321   struct stat statInFile;
    322   if (stat(arg[0], &statInFile) < 0) {
    323     fprintf(stderr, "Unable to stat input file: %s\n", strerror(errno));
    324     return -1;
    325   }
    326 
    327   if (!S_ISREG(statInFile.st_mode)) {
    328     fprintf(stderr, "Input file should be a regular file.\n");
    329     return -1;
    330   }
    331 
    332   InFile = arg[0];
    333   return 0;
    334 }
    335 
    336 static int optSetOutput(int, char **arg) {
    337   char *lastSlash = strrchr(arg[1], '/');
    338   if ((lastSlash != NULL) && *(lastSlash + 1) == '\0') {
    339     fprintf(stderr, "bcc: output file should not be a directory.");
    340     return -1;
    341   }
    342 
    343   OutFile = arg[1];
    344   return 1;
    345 }
    346 
    347 static int optSetIntermediateOutput(int, char **arg) {
    348   char *lastSlash = strrchr(arg[1], '/');
    349   if ((lastSlash != NULL) && *(lastSlash + 1) == '\0') {
    350     fprintf(stderr, "bcc: output intermediate file should not be a directory.");
    351     return -1;
    352   }
    353 
    354   IntermediateOutFile = arg[1];
    355   return 1;
    356 }
    357 
    358 static int optOutputReloc(int, char **) {
    359   OutType = OT_Relocatable;
    360   return 0;
    361 }
    362 
    363 static int optSetOutputShared(int, char **) {
    364   OutType = OT_SharedObject;
    365   return 0;
    366 }
    367 
    368 static int optSetOutputPIC(int, char **) {
    369   OutRelocModel = bccRelocPIC;
    370   return 0;
    371 }
    372 
    373 static int optRunRoot(int, char **) {
    374   RunRoot = true;
    375   return 0;
    376 }
    377 
    378 static int optHelp(int, char **) {
    379   printf("Usage: bcc [OPTION]... [input file]\n\n");
    380   for (unsigned i = 0; i < NUM_OPTIONS; i++) {
    381     const struct OptionInfo *opt = &Options[i];
    382 
    383     printf("\t-%s", opt->option_name);
    384     if (opt->argument_desc)
    385       printf(" %s ", opt->argument_desc);
    386     else
    387       printf(" \t ");
    388     printf("\t%s\n", opt->help_message);
    389   }
    390   exit(0);
    391 }
    392