1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Wrapper for the GNU assembler to make it accept the Sun assembler 31 * arguments where possible. 32 * 33 * There are several limitations; the Sun assembler takes multiple 34 * source files, we only take one. 35 * 36 * -b, -s, -xF, -T plain not supported. 37 * -S isn't supported either, because while GNU as does generate 38 * listings with -a, there's no obvious mapping between sub-options. 39 * -K pic, -K PIC not supported either, though it's not clear what 40 * these actually do .. 41 * -Qy (not supported) adds a string to the .comment section 42 * describing the assembler version, while 43 * -Qn (supported) suppresses the string (also the default). 44 * 45 * We also add '-#' support to see invocation lines.. 46 * We also add '-xarch=amd64' in case we need to feed the assembler 47 * something different (or in case we need to invoke a different binary 48 * altogether!) 49 */ 50 51 #include <sys/types.h> 52 #include <sys/wait.h> 53 #include <stdio.h> 54 #include <unistd.h> 55 #include <string.h> 56 #include <stdlib.h> 57 58 static const char *progname; 59 static int verbose; 60 61 struct aelist { 62 int ael_argc; 63 struct ae { 64 struct ae *ae_next; 65 char *ae_arg; 66 } *ael_head, *ael_tail; 67 }; 68 69 static struct aelist * 70 newael(void) 71 { 72 return (calloc(sizeof (struct aelist), 1)); 73 } 74 75 static void 76 newae(struct aelist *ael, const char *arg) 77 { 78 struct ae *ae; 79 80 ae = calloc(sizeof (*ae), 1); 81 ae->ae_arg = strdup(arg); 82 if (ael->ael_tail == NULL) 83 ael->ael_head = ae; 84 else 85 ael->ael_tail->ae_next = ae; 86 ael->ael_tail = ae; 87 ael->ael_argc++; 88 } 89 90 static void 91 fixae_arg(struct ae *ae, const char *newarg) 92 { 93 free(ae->ae_arg); 94 ae->ae_arg = strdup(newarg); 95 } 96 97 static char ** 98 aeltoargv(struct aelist *ael) 99 { 100 struct ae *ae; 101 char **argv; 102 int argc; 103 104 argv = calloc(sizeof (*argv), ael->ael_argc + 1); 105 106 for (argc = 0, ae = ael->ael_head; ae; ae = ae->ae_next, argc++) { 107 argv[argc] = ae->ae_arg; 108 if (ae == ael->ael_tail) 109 break; 110 } 111 112 return (argv); 113 } 114 115 static int 116 error(const char *arg) 117 { 118 (void) fprintf(stderr, 119 "%s: as->gas mapping failed at or near arg '%s'\n", progname, arg); 120 return (2); 121 } 122 123 static int 124 usage(const char *arg) 125 { 126 if (arg != NULL) 127 (void) fprintf(stderr, "error: %s\n", arg); 128 (void) fprintf(stderr, "Usage: %s [-V] [-#]\n" 129 "\t[-xarch=architecture]\n" 130 "\t[-o objfile] [-L]\n" 131 "\t[-P [[-Ipath] [-Dname] [-Dname=def] [-Uname]]...]\n" 132 "\t[-m] [-n] file.s ...\n", progname); 133 return (3); 134 } 135 136 static void 137 copyuntil(FILE *in, FILE *out, int termchar) 138 { 139 int c; 140 141 while ((c = fgetc(in)) != EOF) { 142 if (out && fputc(c, out) == EOF) 143 exit(1); 144 if (c == termchar) 145 break; 146 } 147 } 148 149 /* 150 * The idea here is to take directives like this emitted 151 * by cpp: 152 * 153 * # num 154 * 155 * and convert them to directives like this that are 156 * understood by the GNU assembler: 157 * 158 * .line num 159 * 160 * and similarly: 161 * 162 * # num "string" optional stuff 163 * 164 * is converted to 165 * 166 * .line num 167 * .file "string" 168 * 169 * While this could be done with a sequence of sed 170 * commands, this is simpler and faster.. 171 */ 172 static pid_t 173 filter(int pipein, int pipeout) 174 { 175 pid_t pid; 176 FILE *in, *out; 177 178 if (verbose) 179 (void) fprintf(stderr, "{#line filter} "); 180 181 switch (pid = fork()) { 182 case 0: 183 if (dup2(pipein, 0) == -1 || 184 dup2(pipeout, 1) == -1) { 185 perror("dup2"); 186 exit(1); 187 } 188 closefrom(3); 189 break; 190 case -1: 191 perror("fork"); 192 default: 193 return (pid); 194 } 195 196 in = fdopen(0, "r"); 197 out = fdopen(1, "w"); 198 199 while (!feof(in)) { 200 int c, num; 201 202 switch (c = fgetc(in)) { 203 case '#': 204 switch (fscanf(in, " %d", &num)) { 205 case 0: 206 /* 207 * discard comment lines completely 208 * discard ident strings completely too. 209 * (GNU as politely ignores them..) 210 */ 211 copyuntil(in, NULL, '\n'); 212 break; 213 default: 214 (void) fprintf(stderr, "fscanf botch?"); 215 /*FALLTHROUGH*/ 216 case EOF: 217 exit(1); 218 /*NOTREACHED*/ 219 case 1: 220 /* 221 * This line has a number at the beginning; 222 * if it has a string after the number, then 223 * it's a filename. 224 */ 225 if (fgetc(in) == ' ' && fgetc(in) == '"') { 226 (void) fprintf(out, "\t.file \""); 227 copyuntil(in, out, '"'); 228 (void) fputc('\n', out); 229 } 230 (void) fprintf(out, "\t.line %d\n", num - 1); 231 /* 232 * discard the rest of the line 233 */ 234 copyuntil(in, NULL, '\n'); 235 break; 236 } 237 break; 238 case '\n': 239 /* 240 * preserve newlines 241 */ 242 (void) fputc(c, out); 243 break; 244 case EOF: 245 /* 246 * don't write EOF! 247 */ 248 break; 249 default: 250 /* 251 * lines that don't begin with '#' are copied 252 */ 253 (void) fputc(c, out); 254 copyuntil(in, out, '\n'); 255 break; 256 } 257 258 if (ferror(out)) 259 exit(1); 260 } 261 262 exit(0); 263 /*NOTREACHED*/ 264 } 265 266 static pid_t 267 invoke(char **argv, int pipein, int pipeout) 268 { 269 pid_t pid; 270 271 if (verbose) { 272 char **dargv = argv; 273 274 while (*dargv) 275 (void) fprintf(stderr, "%s ", *dargv++); 276 } 277 278 switch (pid = fork()) { 279 case 0: 280 if (pipein >= 0 && dup2(pipein, 0) == -1) { 281 perror("dup2"); 282 exit(1); 283 } 284 if (pipeout >= 0 && dup2(pipeout, 1) == -1) { 285 perror("dup2"); 286 exit(1); 287 } 288 closefrom(3); 289 (void) execvp(argv[0], argv); 290 perror("execvp"); 291 (void) fprintf(stderr, "%s: couldn't run %s\n", 292 progname, argv[0]); 293 break; 294 case -1: 295 perror("fork"); 296 default: 297 return (pid); 298 } 299 exit(2); 300 /*NOTREACHED*/ 301 } 302 303 static int 304 pipeline(char **ppargv, char **asargv) 305 { 306 int pipedes[4]; 307 int active = 0; 308 int rval = 0; 309 pid_t pid_pp, pid_f, pid_as; 310 311 if (pipe(pipedes) == -1 || pipe(pipedes + 2) == -1) { 312 perror("pipe"); 313 return (4); 314 } 315 316 if ((pid_pp = invoke(ppargv, -1, pipedes[0])) > 0) 317 active++; 318 319 if (verbose) 320 (void) fprintf(stderr, "| "); 321 322 if ((pid_f = filter(pipedes[1], pipedes[2])) > 0) 323 active++; 324 325 if (verbose) 326 (void) fprintf(stderr, "| "); 327 328 if ((pid_as = invoke(asargv, pipedes[3], -1)) > 0) 329 active++; 330 331 if (verbose) { 332 (void) fprintf(stderr, "\n"); 333 (void) fflush(stderr); 334 } 335 336 closefrom(3); 337 338 if (active != 3) 339 return (5); 340 341 while (active != 0) { 342 pid_t pid; 343 int stat; 344 345 if ((pid = wait(&stat)) == -1) { 346 rval++; 347 break; 348 } 349 350 if (!WIFEXITED(stat)) 351 continue; 352 353 if (pid == pid_pp || pid == pid_f || pid == pid_as) { 354 active--; 355 if (WEXITSTATUS(stat) != 0) 356 rval++; 357 } 358 } 359 360 return (rval); 361 } 362 363 int 364 main(int argc, char *argv[]) 365 { 366 struct aelist *cpp = NULL; 367 struct aelist *m4 = NULL; 368 struct aelist *as = newael(); 369 char **asargv; 370 char *outfile = NULL; 371 char *srcfile = NULL; 372 const char *as_dir, *as64_dir, *m4_dir, *m4_lib_dir, *cpp_dir; 373 char *as_pgm, *as64_pgm, *m4_pgm, *m4_cmdefs, *cpp_pgm; 374 int as64 = 0; 375 int code; 376 377 if ((progname = strrchr(argv[0], '/')) == NULL) 378 progname = argv[0]; 379 else 380 progname++; 381 382 /* 383 * Helpful when debugging, or when changing tool versions.. 384 */ 385 if ((as_dir = getenv("AW_AS_DIR")) == NULL) 386 as_dir = DEFAULT_AS_DIR; /* /usr/sfw/bin */ 387 as_pgm = malloc(strlen(as_dir) + strlen("/gas") + 1); 388 (void) sprintf(as_pgm, "%s/gas", as_dir); 389 390 if ((as64_dir = getenv("AW_AS64_DIR")) == NULL) 391 as64_dir = DEFAULT_AS64_DIR; /* /usr/sfw/bin */ 392 as64_pgm = malloc(strlen(as64_dir) + strlen("/gas") + 1); 393 (void) sprintf(as64_pgm, "%s/gas", as64_dir); 394 395 if ((m4_dir = getenv("AW_M4_DIR")) == NULL) 396 m4_dir = DEFAULT_M4_DIR; /* /usr/ccs/bin */ 397 m4_pgm = malloc(strlen(m4_dir) + strlen("/m4") + 1); 398 (void) sprintf(m4_pgm, "%s/m4", m4_dir); 399 400 if ((m4_lib_dir = getenv("AW_M4LIB_DIR")) == NULL) 401 m4_lib_dir = DEFAULT_M4LIB_DIR; /* /usr/ccs/lib */ 402 m4_cmdefs = malloc(strlen(m4_lib_dir) + strlen("/cmdefs") + 1); 403 (void) sprintf(m4_cmdefs, "%s/cmdefs", m4_lib_dir); 404 405 if ((cpp_dir = getenv("AW_CPP_DIR")) == NULL) 406 cpp_dir = DEFAULT_CPP_DIR; /* /usr/ccs/lib */ 407 cpp_pgm = malloc(strlen(cpp_dir) + strlen("/cpp") + 1); 408 (void) sprintf(cpp_pgm, "%s/cpp", cpp_dir); 409 410 newae(as, as_pgm); 411 newae(as, "--warn"); 412 newae(as, "--fatal-warnings"); 413 newae(as, "--traditional-format"); 414 415 /* 416 * This is a support hack to rewrite code for the compiler 417 * which should probably cause an assembler programmer to recode 418 * - so, generate a warning in this case. 419 */ 420 newae(as, "-K"); 421 422 /* 423 * Walk the argument list, translating as we go .. 424 */ 425 while (--argc > 0) { 426 char *arg; 427 int arglen; 428 429 arg = *++argv; 430 arglen = strlen(arg); 431 432 if (*arg != '-') { 433 char *filename; 434 435 /* 436 * filenames ending in '.s' are taken to be 437 * assembler files, and provide the default 438 * basename of the output file. 439 * 440 * other files are passed through to the 441 * preprocessor, if present, or to gas if not. 442 */ 443 filename = arg; 444 if (arglen > 2 && 445 strcmp(arg + arglen - 2, ".s") == 0) { 446 /* 447 * Though 'as' allows multiple assembler 448 * files to be processed in one invocation 449 * of the assembler, ON only processes one 450 * file at a time, which makes things a lot 451 * simpler! 452 */ 453 if (srcfile == NULL) 454 srcfile = arg; 455 else 456 return (usage( 457 "one assembler file at a time")); 458 459 /* 460 * If we haven't seen a -o option yet, 461 * default the output to the basename 462 * of the input, substituting a .o on the end 463 */ 464 if (outfile == NULL) { 465 char *argcopy; 466 467 argcopy = strdup(arg); 468 argcopy[arglen - 1] = 'o'; 469 470 if ((outfile = strrchr( 471 argcopy, '/')) == NULL) 472 outfile = argcopy; 473 else 474 outfile++; 475 } 476 } 477 if (cpp) 478 newae(cpp, filename); 479 else if (m4) 480 newae(m4, filename); 481 else 482 newae(as, filename); 483 continue; 484 } else 485 arglen--; 486 487 switch (arg[1]) { 488 case 'K': 489 /* 490 * -K pic 491 * -K PIC 492 */ 493 if (arglen == 1) { 494 if ((arg = *++argv) == NULL || *arg == '\0') 495 return (usage("malformed -K")); 496 argc--; 497 } else { 498 arg += 2; 499 } 500 if (strcmp(arg, "PIC") != 0 && strcmp(arg, "pic") != 0) 501 return (usage("malformed -K")); 502 break; /* just ignore -Kpic for gcc */ 503 case 'Q': 504 if (strcmp(arg, "-Qn") == 0) 505 break; 506 /*FALLTHROUGH*/ 507 case 'b': 508 case 's': 509 case 'T': 510 /* 511 * -b Extra symbol table for source browser .. 512 * not relevant to gas, thus should error. 513 * -s Put stabs in .stabs section not stabs.excl 514 * not clear if there's an equivalent 515 * -T 4.x migration option 516 */ 517 default: 518 return (error(arg)); 519 case 'x': 520 /* 521 * Accept -xarch special case to invoke alternate 522 * assemblers or assembler flags for different 523 * architectures. 524 */ 525 if (strcmp(arg, "-xarch=amd64") == 0 || 526 strcmp(arg, "-xarch=generic64") == 0) { 527 as64++; 528 fixae_arg(as->ael_head, as64_pgm); 529 break; 530 } 531 /* 532 * XX64: Is this useful to gas? 533 */ 534 if (strcmp(arg, "-xmodel=kernel") == 0) 535 break; 536 537 /* 538 * -xF Generates performance analysis data 539 * no equivalent 540 */ 541 return (error(arg)); 542 case 'V': 543 newae(as, arg); 544 break; 545 case '#': 546 verbose++; 547 break; 548 case 'L': 549 newae(as, "--keep-locals"); 550 break; 551 case 'n': 552 newae(as, "--no-warn"); 553 break; 554 case 'o': 555 if (arglen != 1) 556 return (usage("bad -o flag")); 557 if ((arg = *++argv) == NULL || *arg == '\0') 558 return (usage("bad -o flag")); 559 outfile = arg; 560 argc--; 561 arglen = strlen(arg + 1); 562 break; 563 case 'm': 564 if (cpp) 565 return (usage("-m conflicts with -P")); 566 if (m4 == NULL) { 567 m4 = newael(); 568 newae(m4, m4_pgm); 569 newae(m4, m4_cmdefs); 570 } 571 break; 572 case 'P': 573 if (m4) 574 return (usage("-P conflicts with -m")); 575 if (cpp == NULL) { 576 cpp = newael(); 577 newae(cpp, cpp_pgm); 578 newae(cpp, "-D__GNUC_AS__"); 579 } 580 break; 581 case 'D': 582 case 'U': 583 if (cpp) 584 newae(cpp, arg); 585 else if (m4) 586 newae(m4, arg); 587 else 588 newae(as, arg); 589 break; 590 case 'I': 591 if (cpp) 592 newae(cpp, arg); 593 else 594 newae(as, arg); 595 break; 596 case '-': /* a gas-specific option */ 597 newae(as, arg); 598 break; 599 } 600 } 601 602 #if defined(__i386) 603 if (as64) 604 newae(as, "--64"); 605 else 606 newae(as, "--32"); 607 #endif 608 609 if (srcfile == NULL) 610 return (usage("no source file(s) specified")); 611 if (outfile == NULL) 612 outfile = "a.out"; 613 newae(as, "-o"); 614 newae(as, outfile); 615 616 asargv = aeltoargv(as); 617 if (cpp) { 618 #if defined(__sparc) 619 newae(cpp, "-Dsparc"); 620 newae(cpp, "-D__sparc"); 621 if (as64) 622 newae(cpp, "-D__sparcv9"); 623 else 624 newae(cpp, "-D__sparcv8"); 625 #elif defined(__i386) || defined(__x86) 626 if (as64) { 627 newae(cpp, "-D__x86_64"); 628 newae(cpp, "-D__amd64"); 629 } else { 630 newae(cpp, "-Di386"); 631 newae(cpp, "-D__i386"); 632 } 633 #else 634 #error "need isa-dependent defines" 635 #endif 636 code = pipeline(aeltoargv(cpp), asargv); 637 } else if (m4) 638 code = pipeline(aeltoargv(m4), asargv); 639 else { 640 /* 641 * XXX should arrange to fork/exec so that we 642 * can unlink the output file if errors are 643 * detected.. 644 */ 645 (void) execvp(asargv[0], asargv); 646 perror("execvp"); 647 (void) fprintf(stderr, "%s: couldn't run %s\n", 648 progname, asargv[0]); 649 code = 7; 650 } 651 if (code != 0) 652 (void) unlink(outfile); 653 return (code); 654 } 655