• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
run(RootPtr rootFunc)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 
parseOption(int argc,char ** argv)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 
loadScript()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 
runRoot(BCCScriptRef script)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 
main(int argc,char ** argv)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__)
optSetTriple(int,char ** arg)313 static int optSetTriple(int, char **arg) {
314   TARGET_TRIPLE_STRING = arg[1];
315   return 1;
316 }
317 #endif
318 
optSetInput(int,char ** arg)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 
optSetOutput(int,char ** arg)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 
optSetIntermediateOutput(int,char ** arg)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 
optOutputReloc(int,char **)358 static int optOutputReloc(int, char **) {
359   OutType = OT_Relocatable;
360   return 0;
361 }
362 
optSetOutputShared(int,char **)363 static int optSetOutputShared(int, char **) {
364   OutType = OT_SharedObject;
365   return 0;
366 }
367 
optSetOutputPIC(int,char **)368 static int optSetOutputPIC(int, char **) {
369   OutRelocModel = bccRelocPIC;
370   return 0;
371 }
372 
optRunRoot(int,char **)373 static int optRunRoot(int, char **) {
374   RunRoot = true;
375   return 0;
376 }
377 
optHelp(int,char **)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