1 /* A sensible interface to MIPS cross compilation tools.
2 Copyright 2001, 2003, 2004 Brian R. Gaeke.
3
4 This file is part of VMIPS.
5
6 VMIPS is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10
11 VMIPS is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with VMIPS; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19
20 /* For later: ways to get page size:
21 long sysconf(_SC_PAGE_SIZE);
22 or
23 long sysconf(_SC_PAGESIZE);
24 or
25 int getpagesize(void);
26 */
27
28 #include "endiantest.h"
29 #include "fileutils.h"
30 #include "stub-dis.h"
31 #include "error.h"
32 #include "options.h"
33 #include <cerrno>
34 #include <cstdarg>
35 #include <climits>
36 #include <cstdlib>
37 #include <cstring>
38 #include <cstdio>
39 #include <iostream>
40 #include <sys/stat.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 #include <vector>
44
45 const unsigned int MAXARGV = 500;
46 const unsigned int TMPFNAMESIZE = 26;
47
48 int verbose = 0;
49 int dryrun = 0;
50
51 char pkgdatadir[PATH_MAX];
52 char ccname[PATH_MAX];
53 char ldname[PATH_MAX];
54 char objcopyname[PATH_MAX];
55 char objdumpname[PATH_MAX];
56 char endianflag[4];
57
program_available(const char * execname)58 bool program_available (const char *execname) {
59 struct stat st;
60 if (stat (execname, &st) < 0)
61 return false;
62 if (!(st.st_mode & S_IXUSR))
63 return false;
64 return true;
65 }
66
config_error(const char * programname,const char * execname)67 int config_error (const char *programname, const char *execname) {
68 fprintf (stderr, "vmipstool: Couldn't find MIPS tool '%s' in '%s' "
69 "(installation error?)\n", programname, execname);
70 return 1;
71 }
72
setup_paths(bool bigendian,const char * mipstoolprefix)73 void setup_paths (bool bigendian, const char *mipstoolprefix) {
74 strcpy (pkgdatadir, PKGDATADIR);
75 // Allow user (or a shell script) to override the path from which the
76 // linker script is read. This allows vmipstool to be executed from the
77 // source tree without vmips being installed.
78 char *user_supplied_pkgdatadir;
79 user_supplied_pkgdatadir = getenv ("VMIPS_PKGDATADIR");
80 if (user_supplied_pkgdatadir) {
81 strncpy (pkgdatadir, user_supplied_pkgdatadir, PATH_MAX);
82 if (strlen (user_supplied_pkgdatadir) >= PATH_MAX)
83 pkgdatadir[PATH_MAX - 1] = '\0';
84 }
85
86 strcpy (ccname, mipstoolprefix);
87 strcat (ccname, "gcc");
88 strcpy (ldname, mipstoolprefix);
89 strcat (ldname, "ld");
90 strcpy (objcopyname, mipstoolprefix);
91 strcat (objcopyname, "objcopy");
92 strcpy (objdumpname, mipstoolprefix);
93 strcat (objdumpname, "objdump");
94
95 // Decide which flag should be used to force the endianness of an
96 // object file.
97 if (bigendian) {
98 strcpy (endianflag, "-EB");
99 } else {
100 strcpy (endianflag, "-EL");
101 }
102 }
103
104 /* ROMs are padded to a multiple of this many bytes. I don't think this
105 is strictly necessary and will probably go away eventually. */
106 long pagesz = 4096;
107
108 /********************************* Various utils ******************************/
109
usage(void)110 void usage(void) {
111 puts("usage:");
112 puts(" vmipstool [ VT-FLAGS ] --compile [ FLAGS ] FILE.c -o FILE.o");
113 puts(" vmipstool [ VT-FLAGS ] --preprocess [ FLAGS ] FILE");
114 puts(" vmipstool [ VT-FLAGS ] --assemble [ FLAGS ] FILE.s -o FILE.o");
115 puts
116 (" vmipstool [ VT-FLAGS ] --link [ FLAGS ] FILE1.o ... FILEn.o -o PROG");
117 puts(" vmipstool [ VT-FLAGS ] --make-rom PROG PROG.rom");
118 puts(" vmipstool [ VT-FLAGS ] --disassemble-rom PROG.rom");
119 puts(" vmipstool [ VT-FLAGS ] --disassemble PROG (or FILE.o)");
120 puts(" vmipstool [ VT-FLAGS ] --swap-words INPUT OUTPUT");
121 puts("");
122 puts("VT-FLAGS may include:");
123 puts(" --help Display this help message and exit.");
124 puts(" --version Display the version of vmipstool and exit.");
125 puts(" --verbose Echo commands as they are run.");
126 puts
127 (" --dry-run Don't actually run anything; use with --verbose.");
128 puts(" --ld-script=T Use T as ld script (instead of default script);");
129 puts(" use with --link.");
130 puts("");
131 puts("Report bugs to <vmips@dgate.org>.");
132 }
133
maybe_echo(char ** newargv)134 void maybe_echo(char **newargv) {
135 int i;
136 if (verbose) {
137 for (i = 0; newargv[i] != NULL; i++)
138 fprintf(stderr, "%s ", newargv[i]);
139 fprintf(stderr, "\n");
140 }
141 }
142
maybe_run(char ** newargv)143 int maybe_run(char **newargv) {
144 pid_t pid;
145 int status, error = 0;
146
147 if (!dryrun) {
148 if ((pid = fork()) == 0) {
149 execv(newargv[0], newargv);
150 perror (newargv[0]);
151 exit (255);
152 } else {
153 waitpid(pid, &status, 0);
154 if (WIFEXITED(status))
155 error = (WEXITSTATUS(status) != 0);
156 else
157 error = 1;
158 }
159 }
160 return error;
161 }
162
echo_and_run_l(char * c,...)163 int echo_and_run_l(char *c, ...) {
164 va_list ap;
165 char *newargv[MAXARGV];
166 int newargc = 0;
167
168 va_start(ap, c);
169 newargv[newargc++] = c;
170 while (c != NULL) {
171 c = va_arg(ap, char *);
172 newargv[newargc++] = c;
173 }
174 va_end(ap);
175
176 maybe_echo(newargv);
177 return maybe_run(newargv);
178 }
179
echo_and_run_lv(char * c,...)180 int echo_and_run_lv(char *c, ...) {
181 va_list ap;
182 char *newargv[MAXARGV];
183 char **originalargv;
184 int newargc = 0;
185 int i;
186
187 va_start(ap, c);
188 newargv[newargc++] = c;
189 while (c != NULL) {
190 c = va_arg(ap, char *);
191 newargv[newargc++] = c;
192 }
193 newargc--;
194 originalargv = va_arg(ap, char **);
195 for (i = 0; originalargv[i] != NULL; i++)
196 newargv[newargc++] = originalargv[i];
197 newargv[newargc++] = NULL;
198 va_end(ap);
199
200 maybe_echo(newargv);
201 return maybe_run(newargv);
202 }
203
204 /* Search colon-separated PATH for file named TARGET. Copy found file's
205 full pathname into RESULT and return true if found. Returns false and
206 leaves result untouched if not found. */
search_path(char * result,char * path,const char * target)207 bool search_path(char *result, char *path, const char *target) {
208 const char *delims = ":";
209 char *dir = strtok (path, delims);
210 while (dir != NULL) {
211 char trypath[PATH_MAX];
212 sprintf (trypath, "%s/%s", dir, target);
213 if (can_read_file (trypath)) {
214 strcpy (result, trypath);
215 return true;
216 } else {
217 dir = strtok (NULL, delims);
218 }
219 }
220 return false;
221 }
222
223 /********************************* Linking ******************************/
224
225 char ldscript_full_path[PATH_MAX];
226 bool ldscript_error = false;
227
can_read_default_ldscript()228 int can_read_default_ldscript () {
229 /* Build the search path */
230 char *ldscript_search_path;
231
232 /* so that it can work without being installed */
233 char rest_of_path[] =
234 ":.:..:./sample_code:../sample_code:../../sample_code";
235 const char ldscript_name[] = "ld.script";
236
237 ldscript_search_path =
238 new char[strlen(rest_of_path) + strlen(pkgdatadir) + 2];
239 strcpy(ldscript_search_path, pkgdatadir);
240 strcat(ldscript_search_path, rest_of_path);
241
242 /* Look for the ld.script */
243 return search_path(ldscript_full_path, ldscript_search_path,
244 ldscript_name);
245 }
246
do_link(int argc,char ** argv)247 int do_link (int argc, char **argv) {
248 if (ldscript_error) {
249 fprintf(stderr, "vmipstool: can't access linker script\n");
250 return 1;
251 }
252 if (!program_available (ldname))
253 return config_error ("ld", ldname);
254 return echo_and_run_lv (ldname, endianflag, "-T", ldscript_full_path, NULL,
255 argv, NULL);
256 }
257
258 /********************************* ROM-making ******************************/
259
copy_with_padded_blocks(char * in,char * out,long size)260 int copy_with_padded_blocks (char *in, char *out, long size) {
261 char *buff = new char[size];
262 FILE *f = fopen(in, "rb");
263 if (!f) {
264 perror(in);
265 return -1;
266 }
267 FILE *g = fopen(out, "wb");
268 if (!g) {
269 perror(out);
270 return -1;
271 }
272 errno = 0;
273 int readcount;
274 while ((readcount = fread(buff, 1, size, f)) != 0) {
275 if (readcount < size) {
276 if (errno) {
277 perror(in);
278 fclose(f);
279 fclose(g);
280 return -1;
281 } else {
282 for (int i = readcount; i < size; i++)
283 buff[i] = '\0';
284 }
285 }
286 int writecount = fwrite(buff, 1, size, g);
287 if (writecount < size) {
288 perror(out);
289 fclose(f);
290 fclose(g);
291 return -1;
292 }
293 }
294 fclose(f);
295 fclose(g);
296 delete [] buff;
297 return 0;
298 }
299
make_rom(int argc,char ** argv)300 int make_rom (int argc, char **argv) {
301 char tmp[TMPFNAMESIZE];
302 char *inputfile;
303 char *outputfile;
304 if (argc != 2) {
305 fprintf(stderr, "vmipstool: --make-rom takes 2 arguments\n");
306 usage();
307 return 1;
308 }
309 sprintf (tmp, "/tmp/vmipstool-%d-1", (int) getpid ());
310 inputfile = argv[0];
311 outputfile = argv[1];
312 if (!program_available (objcopyname))
313 return config_error ("objcopy", objcopyname);
314 if (echo_and_run_l (objcopyname, "-O", "binary", inputfile, tmp, NULL) != 0) {
315 fprintf(stderr, "vmipstool: Error creating binary image of program. Aborting.\n");
316 return 1;
317 }
318 if (verbose)
319 printf ("dd if=%s of=%s bs=%ld conv=sync > /dev/null 2>&1\n",
320 tmp, outputfile, pagesz);
321 if (copy_with_padded_blocks(tmp, outputfile, pagesz) != 0) {
322 fprintf(stderr,
323 "vmipstool: Error copying program image to ROM file. Aborting.\n");
324 return 1;
325 }
326 if (verbose)
327 printf("rm %s\n", tmp);
328 unlink(tmp);
329 return 0;
330 }
331
332 /********************************* Byte-swapping ******************************/
333
swap_words(int argc,char ** argv)334 int swap_words (int argc, char **argv) {
335 char *inputfname = argv[0];
336 char *outputfname = argv[1];
337 FILE *inputfp = fopen(inputfname, "rb");
338 if (!inputfp) {
339 perror(inputfname);
340 return -1;
341 }
342 FILE *outputfp = fopen(outputfname, "wb");
343 if (!outputfp) {
344 perror(outputfname);
345 return -1;
346 }
347 char buff[4], buff2[4];
348 int readcount, writecount;
349 while ((readcount = fread(buff, 1, 4, inputfp)) > 0) {
350 if (readcount < 4) {
351 fprintf(stderr,
352 "%s: warning: file does not end on a word boundary\n",
353 inputfname);
354 for (int i = readcount; i < 3; i++)
355 buff[i] = 0;
356 }
357 buff2[0] = buff[3];
358 buff2[1] = buff[2];
359 buff2[2] = buff[1];
360 buff2[3] = buff[0];
361 if ((writecount = fwrite(buff2, 1, 4, outputfp)) != 4) {
362 perror(outputfname);
363 break;
364 }
365 }
366 fclose(inputfp);
367 fclose(outputfp);
368 return 0;
369 }
370
371 /******************************** Everything else *****************************/
372
compile(int argc,char ** argv)373 int compile (int argc, char **argv) {
374 if (!program_available (ccname)) return config_error ("gcc", ccname);
375 return echo_and_run_lv (ccname, endianflag, "-mno-abicalls",
376 "-fno-pic", NULL, argv, NULL);
377 }
378
assemble(int argc,char ** argv)379 int assemble (int argc, char **argv) {
380 if (!program_available (ccname)) return config_error ("gcc", ccname);
381 return echo_and_run_lv (ccname, "-c", "-x", "assembler-with-cpp",
382 endianflag, "-fno-pic", NULL, argv, NULL);
383 }
384
preprocess(int argc,char ** argv)385 int preprocess (int argc, char **argv) {
386 if (!program_available (ccname)) return config_error ("gcc", ccname);
387 return echo_and_run_lv(ccname, "-E", NULL, argv, NULL);
388 }
389
disassemble(int argc,char ** argv)390 int disassemble (int argc, char **argv) {
391 char *inputfile = argv[0];
392 if (!program_available (objdumpname))
393 return config_error ("objdump", objdumpname);
394 return echo_and_run_l (objdumpname, "--disassemble", endianflag,
395 inputfile, NULL);
396 }
397
disassemble_rom(int argc,char ** argv)398 int disassemble_rom (int argc, char **argv) {
399 char *inputfile = argv[0];
400 if (!program_available (objdumpname))
401 return config_error ("objdump", objdumpname);
402 return echo_and_run_l (objdumpname, "--disassemble-all", "--target=binary",
403 endianflag, "-m", "mips", inputfile, NULL);
404 }
405
disassemble_word(int argc,char ** argv)406 int disassemble_word (int argc, char **argv) {
407 EndianSelfTester est;
408 Disassembler d (est.host_is_big_endian(), stdout);
409 if (argc != 2) {
410 fprintf (stderr, "Error: must supply PC and INSTR\n");
411 return 1;
412 }
413 uint32 pc = strtoul(argv[0], NULL, 0);
414 uint32 instr = strtoul(argv[1], NULL, 0);
415 d.disassemble (pc, instr);
416 return 0;
417 }
418
version(int argc,char ** argv)419 int version (int argc, char **argv) {
420 printf("vmipstool (VMIPS) %s\n", VERSION);
421 return 0;
422 }
423
help(int argc,char ** argv)424 int help (int argc, char **argv) {
425 usage();
426 return 0;
427 }
428
429 /******************************** Options handling ****************************/
430
431 int (*handler) (int argc, char **argv);
432 char **handler_argv = 0;
433 int handler_argc = -1;
434
435 class VmipstoolOptions : public Options {
436 public:
VmipstoolOptions()437 VmipstoolOptions () : Options () {
438 }
~VmipstoolOptions()439 virtual ~VmipstoolOptions () { }
440
usage(char * argv0)441 virtual void usage (char *argv0)
442 {
443 printf ("usage:\n"
444 " %s [ VT-FLAGS ] MODE MODE-ARGS\n"
445 "Each MODE takes its own MODE-ARGS:\n"
446 " MODE: MODE-ARGS:\n"
447 " --compile [ FLAGS ] FILE.c -o FILE.o\n"
448 " --preprocess [ FLAGS ] FILE\n"
449 " --assemble [ FLAGS ] FILE.s -o FILE.o\n"
450 " --link [ FLAGS ] FILE1.o ... FILEn.o -o PROG\n"
451 " --make-rom PROG PROG.rom\n"
452 " --disassemble-rom PROG.rom\n"
453 " --disassemble-word PC INSTR\n"
454 " --disassemble PROG (or FILE.o)\n"
455 " --swap-words INPUT OUTPUT\n"
456 "\n"
457 "VT-FLAGS may include:\n"
458 " --help Display this help message and exit.\n"
459 " --version Display the version of vmipstool and exit.\n"
460 " --verbose Echo commands as they are run.\n"
461 " --dry-run Don't actually run anything; use with --verbose.\n"
462 " --ld-script=T Use T as ld script (instead of default script);\n"
463 " use with --link.\n"
464 "\n" "Report bugs to <vmips@dgate.org>.\n", argv0);
465 }
466
process_options(int argc,char ** argv)467 virtual void process_options (int argc, char **argv)
468 {
469 /* Get options from defaults. */
470 process_defaults ();
471
472 /* Get default name of user's config file */
473 char user_config_filename[PATH_MAX] = "~/.vmipsrc";
474
475 /* Process command line */
476 bool read_system_config_file = true;
477 std::vector<std::string> command_line_options;
478
479 int i;
480 for (i = 1; (!handler) && (i < argc); ++i)
481 {
482 if (strcmp (argv[i], "--version") == 0)
483 {
484 print_package_version ("vmipstool", VERSION);
485 exit (0);
486 }
487 else if (strcmp (argv[i], "--help") == 0)
488 {
489 usage (argv[0]);
490 exit (0);
491 }
492 else if (strcmp (argv[i], "-o") == 0)
493 {
494 if (argc <= i + 1)
495 error_exit ("The -o flag requires an argument. Try %s --help",
496 argv[0]);
497 command_line_options.push_back (argv[i + 1]);
498 ++i;
499 }
500 else if (strcmp (argv[i], "-F") == 0)
501 {
502 if (argc <= i + 1)
503 error_exit ("The -F flag requires an argument. Try %s --help",
504 argv[0]);
505 strcpy (user_config_filename, argv[i + 1]);
506 ++i;
507 }
508 else if (strcmp (argv[i], "-n") == 0)
509 {
510 read_system_config_file = false;
511 }
512 else if (strcmp (argv[i], "--verbose") == 0)
513 {
514 verbose = 1;
515 }
516 else if (strcmp (argv[i], "--dry-run") == 0)
517 {
518 dryrun = 1;
519 }
520 else if (strncmp (argv[i], "--ld-script=", 12) == 0)
521 {
522 strcpy (ldscript_full_path, &argv[i][12]);
523 ldscript_error = !can_read_file (ldscript_full_path);
524 }
525 else if (strcmp (argv[i], "--compile") == 0)
526 {
527 handler = compile;
528 }
529 else if (strcmp (argv[i], "--assemble") == 0)
530 {
531 handler = assemble;
532 }
533 else if (strcmp (argv[i], "--preprocess") == 0)
534 {
535 handler = preprocess;
536 }
537 else if (strcmp (argv[i], "--link") == 0)
538 {
539 handler = do_link;
540 }
541 else if (strcmp (argv[i], "--make-rom") == 0)
542 {
543 handler = make_rom;
544 }
545 else if (strcmp (argv[i], "--disassemble-rom") == 0)
546 {
547 handler = disassemble_rom;
548 }
549 else if (strcmp (argv[i], "--disassemble-word") == 0)
550 {
551 handler = disassemble_word;
552 }
553 else if (strcmp (argv[i], "--disassemble") == 0)
554 {
555 handler = disassemble;
556 }
557 else if (strcmp (argv[i], "--swap-words") == 0)
558 {
559 handler = swap_words;
560 }
561 else /* Unrecognized arg. */
562 {
563 std::cerr << "Unrecognized arg: " << argv[i] << "\n";
564 usage (argv[0]);
565 exit (1);
566 }
567 }
568 if (!handler)
569 {
570 std::cerr << "Error: Missing mode arg.\n";
571 usage (argv[0]); /* Arg list was incomplete somehow. */
572 exit (1);
573 }
574 handler_argc = argc - i;
575 handler_argv = argv + i;
576
577 /* Get options from system configuration file */
578 if (read_system_config_file)
579 process_options_from_file (SYSTEM_CONFIG_FILE);
580
581 /* Get options from user configuration file */
582 tilde_expand (user_config_filename);
583 process_options_from_file (user_config_filename);
584
585 /* Process -o options saved from command line, above. */
586 for (std::vector<std::string>::iterator i =
587 command_line_options.begin (), e = command_line_options.end ();
588 i != e; ++i)
589 process_one_option (i->c_str ());
590 }
591 };
592
593 int
main(int argc,char ** argv)594 main (int argc, char **argv)
595 {
596 /* Find the default ld script. */
597 ldscript_error = !can_read_default_ldscript ();
598
599 /* Get the command line options. */
600 VmipstoolOptions *opt = new VmipstoolOptions;
601 opt->process_options (argc, argv);
602 setup_paths (opt->option ("bigendian")->flag,
603 opt->option ("mipstoolprefix")->str);
604
605 /* Run the appropriate handler. */
606 if (!handler)
607 fatal_error ("handler was not set");
608 if (handler_argc == -1)
609 fatal_error ("handler_argc was not set");
610 if (!handler_argv)
611 fatal_error ("handler_argv was not set");
612 return handler (handler_argc, handler_argv);
613 }
614