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