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