1 
   2 /*
   3  * CDDL HEADER START
   4  *
   5  * The contents of this file are subject to the terms of the
   6  * Common Development and Distribution License (the "License").
   7  * You may not use this file except in compliance 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 /*
  24  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*
  29  * Wrapper for the GNU assembler to make it accept the Sun assembler
  30  * arguments where possible.
  31  *
  32  * There are several limitations; the Sun assembler takes multiple
  33  * source files, we only take one.
  34  *
  35  * -b, -s, -xF, -T plain not supported.
  36  * -S isn't supported either, because while GNU as does generate
  37  * listings with -a, there's no obvious mapping between sub-options.
  38  * -K pic, -K PIC not supported either, though it's not clear what
  39  * these actually do ..
  40  * -Qy (not supported) adds a string to the .comment section
  41  * describing the assembler version, while
  42  * -Qn (supported) suppresses the string (also the default).
  43  *
  44  * We also add '-#' support to see invocation lines..
  45  * We also add '-xarch=amd64' in case we need to feed the assembler
  46  * something different (or in case we need to invoke a different binary
  47  * altogether!)
  48  */
  49 
  50 #include <sys/types.h>
  51 #include <sys/wait.h>
  52 #include <stdio.h>
  53 #include <unistd.h>
  54 #include <string.h>
  55 #include <stdlib.h>
  56 #include <sys/param.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  * Variant of copyuntil(), used for copying the path used
 151  * for .file directives. This version removes the workspace
 152  * from the head of the path, or failing that, attempts to remove
 153  * /usr/include. This is a workaround for the way gas handles
 154  * these directives. The objects produced by gas contain STT_FILE
 155  * symbols for every .file directive. These FILE symbols contain our
 156  * workspace paths, leading to wsdiff incorrectly flagging them as
 157  * having changed. By clipping off the workspace from these paths,
 158  * we eliminate these false positives.
 159  */
 160 static void
 161 copyuntil_path(FILE *in, FILE *out, int termchar,
 162     const char *wspace, size_t wspace_len)
 163 {
 164 #define SYS_INC "/usr/include/"
 165 
 166         static const size_t proto_inc_len = sizeof (PROTO_INC) - 1;
 167         static const size_t sys_inc_len = sizeof (SYS_INC) - 1;
 168 
 169         /*
 170          * Dynamically sized buffer for reading paths. Retained
 171          * and reused between calls.
 172          */
 173         static char     *buf = NULL;
 174         static size_t   bufsize = 0;
 175 
 176         size_t  bufcnt = 0;
 177         char    *bufptr;
 178         int     c;
 179 
 180         /* Read the path into the buffer */
 181         while ((c = fgetc(in)) != EOF) {
 182                 /*
 183                  * If we need a buffer, or need a larger buffer,
 184                  * fix that here.
 185                  */
 186                 if (bufcnt >= bufsize) {
 187                         bufsize = (bufsize == 0) ? MAXPATHLEN : (bufsize * 2);
 188                         buf = realloc(buf, bufsize + 1); /* + room for NULL */
 189                         if (buf == NULL) {
 190                                 perror("realloc");
 191                                 exit(1);
 192                         }
 193                 }
 194 
 195                 buf[bufcnt++] = c;
 196                 if (c == termchar)
 197                         break;
 198         }
 199         if (bufcnt == 0)
 200                 return;
 201 
 202         /*
 203          * We have a non-empty buffer, and thus the opportunity
 204          * to do some surgery on it before passing it to the output.
 205          */
 206         buf[bufcnt] = '\0';
 207         bufptr = buf;
 208 
 209         /*
 210          * If our workspace is at the start, remove it.
 211          * If not, then look for the system /usr/include instead.
 212          */
 213         if ((wspace_len > 0) && (wspace_len < bufcnt) &&
 214             (strncmp(bufptr, wspace, wspace_len) == 0)) {
 215                 bufptr += wspace_len;
 216                 bufcnt -= wspace_len;
 217 
 218                 /*
 219                  * Further opportunity: Also clip the prefix
 220                  * that leads to /usr/include in the proto.
 221                  */
 222                 if ((proto_inc_len < bufcnt) &&
 223                     (strncmp(bufptr, PROTO_INC, proto_inc_len) == 0)) {
 224                         bufptr += proto_inc_len;
 225                         bufcnt -= proto_inc_len;
 226                 }
 227         } else if ((sys_inc_len < bufcnt) &&
 228             (strncmp(bufptr, SYS_INC, sys_inc_len) == 0)) {
 229                 bufptr += sys_inc_len;
 230                 bufcnt -= sys_inc_len;
 231         }
 232 
 233         /* Output whatever is left */
 234         if (out && (fwrite(bufptr, 1, bufcnt, out) != bufcnt)) {
 235                 perror("fwrite");
 236                 exit(1);
 237         }
 238 
 239 #undef PROTO_INC
 240 #undef SYS_INC
 241 }
 242 
 243 /*
 244  * The idea here is to take directives like this emitted
 245  * by cpp:
 246  *
 247  *      # num
 248  *
 249  * and convert them to directives like this that are
 250  * understood by the GNU assembler:
 251  *
 252  *      .line num
 253  *
 254  * and similarly:
 255  *
 256  *      # num "string" optional stuff
 257  *
 258  * is converted to
 259  *
 260  *      .line num
 261  *      .file "string"
 262  *
 263  * While this could be done with a sequence of sed
 264  * commands, this is simpler and faster..
 265  */
 266 static pid_t
 267 filter(int pipein, int pipeout)
 268 {
 269         pid_t pid;
 270         FILE *in, *out;
 271         char *wspace;
 272         size_t wspace_len;
 273 
 274         if (verbose)
 275                 (void) fprintf(stderr, "{#line filter} ");
 276 
 277         switch (pid = fork()) {
 278         case 0:
 279                 if (dup2(pipein, 0) == -1 ||
 280                     dup2(pipeout, 1) == -1) {
 281                         perror("dup2");
 282                         exit(1);
 283                 }
 284                 closefrom(3);
 285                 break;
 286         case -1:
 287                 perror("fork");
 288         default:
 289                 return (pid);
 290         }
 291 
 292         in = fdopen(0, "r");
 293         out = fdopen(1, "w");
 294 
 295         /*
 296          * Key off the CODEMGR_WS environment variable to detect
 297          * if we're in an activated workspace, and to get the
 298          * path to the workspace.
 299          */
 300         wspace = getenv("CODEMGR_WS");
 301         if (wspace != NULL)
 302                 wspace_len = strlen(wspace);
 303 
 304         while (!feof(in)) {
 305                 int c, num;
 306 
 307                 switch (c = fgetc(in)) {
 308                 case '#':
 309                         switch (fscanf(in, " %d", &num)) {
 310                         case 0:
 311                                 /*
 312                                  * discard comment lines completely
 313                                  * discard ident strings completely too.
 314                                  * (GNU as politely ignores them..)
 315                                  */
 316                                 copyuntil(in, NULL, '\n');
 317                                 break;
 318                         default:
 319                                 (void) fprintf(stderr, "fscanf botch?");
 320                                 /*FALLTHROUGH*/
 321                         case EOF:
 322                                 exit(1);
 323                                 /*NOTREACHED*/
 324                         case 1:
 325                                 /*
 326                                  * This line has a number at the beginning;
 327                                  * if it has a string after the number, then
 328                                  * it's a filename.
 329                                  *
 330                                  * If this is an activated workspace, use
 331                                  * copyuntil_path() to do path rewriting
 332                                  * that will prevent workspace paths from
 333                                  * being burned into the resulting object.
 334                                  * If not in an activated workspace, then
 335                                  * copy the existing path straight through
 336                                  * without interpretation.
 337                                  */
 338                                 if (fgetc(in) == ' ' && fgetc(in) == '"') {
 339                                         (void) fprintf(out, "\t.file \"");
 340                                         if (wspace != NULL)
 341                                                 copyuntil_path(in, out, '"',
 342                                                     wspace, wspace_len);
 343                                         else
 344                                                 copyuntil(in, out, '"');
 345                                         (void) fputc('\n', out);
 346                                 }
 347                                 (void) fprintf(out, "\t.line %d\n", num - 1);
 348                                 /*
 349                                  * discard the rest of the line
 350                                  */
 351                                 copyuntil(in, NULL, '\n');
 352                                 break;
 353                         }
 354                         break;
 355                 case '\n':
 356                         /*
 357                          * preserve newlines
 358                          */
 359                         (void) fputc(c, out);
 360                         break;
 361                 case EOF:
 362                         /*
 363                          * don't write EOF!
 364                          */
 365                         break;
 366                 default:
 367                         /*
 368                          * lines that don't begin with '#' are copied
 369                          */
 370                         (void) fputc(c, out);
 371                         copyuntil(in, out, '\n');
 372                         break;
 373                 }
 374 
 375                 if (ferror(out))
 376                         exit(1);
 377         }
 378 
 379         exit(0);
 380         /*NOTREACHED*/
 381 }
 382 
 383 static pid_t
 384 invoke(char **argv, int pipein, int pipeout)
 385 {
 386         pid_t pid;
 387 
 388         if (verbose) {
 389                 char **dargv = argv;
 390 
 391                 while (*dargv)
 392                         (void) fprintf(stderr, "%s ", *dargv++);
 393         }
 394 
 395         switch (pid = fork()) {
 396         case 0:
 397                 if (pipein >= 0 && dup2(pipein, 0) == -1) {
 398                         perror("dup2");
 399                         exit(1);
 400                 }
 401                 if (pipeout >= 0 && dup2(pipeout, 1) == -1) {
 402                         perror("dup2");
 403                         exit(1);
 404                 }
 405                 closefrom(3);
 406                 (void) execvp(argv[0], argv);
 407                 perror("execvp");
 408                 (void) fprintf(stderr, "%s: couldn't run %s\n",
 409                     progname, argv[0]);
 410                 break;
 411         case -1:
 412                 perror("fork");
 413         default:
 414                 return (pid);
 415         }
 416         exit(2);
 417         /*NOTREACHED*/
 418 }
 419 
 420 static int
 421 pipeline(char **ppargv, char **asargv)
 422 {
 423         int pipedes[4];
 424         int active = 0;
 425         int rval = 0;
 426         pid_t pid_pp, pid_f, pid_as;
 427 
 428         if (pipe(pipedes) == -1 || pipe(pipedes + 2) == -1) {
 429                 perror("pipe");
 430                 return (4);
 431         }
 432 
 433         if ((pid_pp = invoke(ppargv, -1, pipedes[0])) > 0)
 434                 active++;
 435 
 436         if (verbose)
 437                 (void) fprintf(stderr, "| ");
 438 
 439         if ((pid_f = filter(pipedes[1], pipedes[2])) > 0)
 440                 active++;
 441 
 442         if (verbose)
 443                 (void) fprintf(stderr, "| ");
 444 
 445         if ((pid_as = invoke(asargv, pipedes[3], -1)) > 0)
 446                 active++;
 447 
 448         if (verbose) {
 449                 (void) fprintf(stderr, "\n");
 450                 (void) fflush(stderr);
 451         }
 452 
 453         closefrom(3);
 454 
 455         if (active != 3)
 456                 return (5);
 457 
 458         while (active != 0) {
 459                 pid_t pid;
 460                 int stat;
 461 
 462                 if ((pid = wait(&stat)) == -1) {
 463                         rval++;
 464                         break;
 465                 }
 466 
 467                 if (!WIFEXITED(stat))
 468                         continue;
 469 
 470                 if (pid == pid_pp || pid == pid_f || pid == pid_as) {
 471                         active--;
 472                         if (WEXITSTATUS(stat) != 0)
 473                                 rval++;
 474                 }
 475         }
 476 
 477         return (rval);
 478 }
 479 
 480 int
 481 main(int argc, char *argv[])
 482 {
 483         struct aelist *cpp = NULL;
 484         struct aelist *m4 = NULL;
 485         struct aelist *as = newael();
 486         char **asargv;
 487         char *outfile = NULL;
 488         char *srcfile = NULL;
 489         const char *dir, *cmd;
 490         static char as_pgm[MAXPATHLEN];
 491         static char as64_pgm[MAXPATHLEN];
 492         static char m4_pgm[MAXPATHLEN];
 493         static char m4_cmdefs[MAXPATHLEN];
 494         static char cpp_pgm[MAXPATHLEN];
 495         int as64 = 0;
 496         int code;
 497 
 498         if ((progname = strrchr(argv[0], '/')) == NULL)
 499                 progname = argv[0];
 500         else
 501                 progname++;
 502 
 503         /*
 504          * Helpful when debugging, or when changing tool versions..
 505          */
 506         cmd = dir = NULL;
 507         if ((cmd = getenv("AW_" AW_TARGET "_AS")) == NULL)
 508                 cmd = getenv("AW_AS");
 509         if ((dir = getenv("AW_" AW_TARGET "_AS_DIR")) == NULL)
 510                 dir = getenv("AW_AS_DIR");
 511 
 512         if (cmd != NULL) {
 513                 (void) strlcpy(as_pgm, cmd, sizeof (as_pgm));
 514         } else {
 515                 if (dir == NULL)
 516                         dir = DEFAULT_AS_DIR;
 517                 (void) snprintf(as_pgm, sizeof (as_pgm), "%s/gas", dir);
 518         }
 519 
 520         cmd = dir = NULL;
 521         if ((cmd = getenv("AW_" AW_TARGET "_AS64")) == NULL)
 522                 cmd = getenv("AW_AS64");
 523         if ((dir = getenv("AW_" AW_TARGET "_AS64_DIR")) == NULL)
 524                 dir = getenv("AW_AS64_DIR");
 525 
 526         if (cmd != NULL) {
 527                 (void) strlcpy(as64_pgm, cmd, sizeof (as64_pgm));
 528         } else {
 529                 if (dir == NULL)
 530                         dir = DEFAULT_AS64_DIR;
 531                 (void) snprintf(as64_pgm, sizeof (as_pgm), "%s/gas", dir);
 532         }
 533 
 534         if ((cmd = getenv("AW_M4")) != NULL)
 535                 strlcpy(m4_pgm, cmd, sizeof (m4_pgm));
 536         else {
 537                 if ((dir = getenv("AW_M4_DIR")) == NULL)
 538                         dir = DEFAULT_M4_DIR;   /* /usr/ccs/bin */
 539                 (void) snprintf(m4_pgm, sizeof (m4_pgm), "%s/m4", dir);
 540         }
 541 
 542         if ((cmd = getenv("AW_M4LIB")) != NULL)
 543                 strlcpy(m4_cmdefs, cmd, sizeof (m4_cmdefs));
 544         else {
 545                 if ((dir = getenv("AW_M4LIB_DIR")) == NULL)
 546                         dir = DEFAULT_M4LIB_DIR;        /* /usr/ccs/lib */
 547                 (void) snprintf(m4_cmdefs, sizeof (m4_cmdefs),
 548                     "%s/cm4defs", dir);
 549         }
 550 
 551         if ((cmd = getenv("AW_CPP")) != NULL)
 552                 strlcpy(cpp_pgm, cmd, sizeof (cpp_pgm));
 553         else {
 554                 if ((dir = getenv("AW_CPP_DIR")) == NULL)
 555                         dir = DEFAULT_CPP_DIR;  /* /usr/ccs/lib */
 556                 (void) snprintf(cpp_pgm, sizeof (cpp_pgm), "%s/cpp", dir);
 557         }
 558 
 559         newae(as, as_pgm);
 560         newae(as, "--warn");
 561         newae(as, "--fatal-warnings");
 562         newae(as, "--traditional-format");
 563 
 564         /*
 565          * Walk the argument list, translating as we go ..
 566          */
 567         while (--argc > 0) {
 568                 char *arg;
 569                 int arglen;
 570 
 571                 arg = *++argv;
 572                 arglen = strlen(arg);
 573 
 574                 if (*arg != '-') {
 575                         char *filename;
 576 
 577                         /*
 578                          * filenames ending in '.s' are taken to be
 579                          * assembler files, and provide the default
 580                          * basename of the output file.
 581                          *
 582                          * other files are passed through to the
 583                          * preprocessor, if present, or to gas if not.
 584                          */
 585                         filename = arg;
 586                         if ((arglen > 2) &&
 587                             ((strcmp(arg + arglen - 2, ".s") == 0) ||
 588                             (strcmp(arg + arglen - 2, ".S") == 0))) {
 589                                 /*
 590                                  * Though 'as' allows multiple assembler
 591                                  * files to be processed in one invocation
 592                                  * of the assembler, ON only processes one
 593                                  * file at a time, which makes things a lot
 594                                  * simpler!
 595                                  */
 596                                 if (srcfile == NULL)
 597                                         srcfile = arg;
 598                                 else
 599                                         return (usage(
 600                                             "one assembler file at a time"));
 601 
 602                                 /*
 603                                  * If we haven't seen a -o option yet,
 604                                  * default the output to the basename
 605                                  * of the input, substituting a .o on the end
 606                                  */
 607                                 if (outfile == NULL) {
 608                                         char *argcopy;
 609 
 610                                         argcopy = strdup(arg);
 611                                         argcopy[arglen - 1] = 'o';
 612 
 613                                         if ((outfile = strrchr(
 614                                             argcopy, '/')) == NULL)
 615                                                 outfile = argcopy;
 616                                         else
 617                                                 outfile++;
 618                                 }
 619                         }
 620                         if (cpp)
 621                                 newae(cpp, filename);
 622                         else if (m4)
 623                                 newae(m4, filename);
 624                         else
 625                                 newae(as, filename);
 626                         continue;
 627                 } else
 628                         arglen--;
 629 
 630                 switch (arg[1]) {
 631                 case 'K':
 632                         /*
 633                          * -K pic
 634                          * -K PIC
 635                          */
 636                         if (arglen == 1) {
 637                                 if ((arg = *++argv) == NULL || *arg == '\0')
 638                                         return (usage("malformed -K"));
 639                                 argc--;
 640                         } else {
 641                                 arg += 2;
 642                         }
 643                         if (strcmp(arg, "PIC") != 0 && strcmp(arg, "pic") != 0)
 644                                 return (usage("malformed -K"));
 645                         break;          /* just ignore -Kpic for gcc */
 646                 case 'Q':
 647                         if (strcmp(arg, "-Qn") == 0)
 648                                 break;
 649                         /*FALLTHROUGH*/
 650                 case 'b':
 651                 case 's':
 652                 case 'T':
 653                         /*
 654                          * -b   Extra symbol table for source browser ..
 655                          *      not relevant to gas, thus should error.
 656                          * -s   Put stabs in .stabs section not stabs.excl
 657                          *      not clear if there's an equivalent
 658                          * -T   4.x migration option
 659                          */
 660                 default:
 661                         return (error(arg));
 662                 case 'x':
 663                         /*
 664                          * Accept -xarch special case to invoke alternate
 665                          * assemblers or assembler flags for different
 666                          * architectures.
 667                          */
 668                         if (strcmp(arg, "-xarch=amd64") == 0 ||
 669                             strcmp(arg, "-xarch=generic64") == 0) {
 670                                 as64++;
 671                                 fixae_arg(as->ael_head, as64_pgm);
 672                                 break;
 673                         }
 674                         /*
 675                          * XX64: Is this useful to gas?
 676                          */
 677                         if (strcmp(arg, "-xmodel=kernel") == 0)
 678                                 break;
 679 
 680                         /*
 681                          * -xF  Generates performance analysis data
 682                          *      no equivalent
 683                          */
 684                         return (error(arg));
 685                 case 'V':
 686                         newae(as, arg);
 687                         break;
 688                 case '#':
 689                         verbose++;
 690                         break;
 691                 case 'L':
 692                         newae(as, "--keep-locals");
 693                         break;
 694                 case 'n':
 695                         newae(as, "--no-warn");
 696                         break;
 697                 case 'o':
 698                         if (arglen != 1)
 699                                 return (usage("bad -o flag"));
 700                         if ((arg = *++argv) == NULL || *arg == '\0')
 701                                 return (usage("bad -o flag"));
 702                         outfile = arg;
 703                         argc--;
 704                         arglen = strlen(arg + 1);
 705                         break;
 706                 case 'm':
 707                         if (cpp)
 708                                 return (usage("-m conflicts with -P"));
 709                         if (m4 == NULL) {
 710                                 m4 = newael();
 711                                 newae(m4, m4_pgm);
 712                                 newae(m4, m4_cmdefs);
 713                         }
 714                         break;
 715                 case 'P':
 716                         if (m4)
 717                                 return (usage("-P conflicts with -m"));
 718                         if (cpp == NULL) {
 719                                 cpp = newael();
 720                                 newae(cpp, cpp_pgm);
 721                                 newae(cpp, "-D__GNUC_AS__");
 722                         }
 723                         break;
 724                 case 'D':
 725                 case 'U':
 726                         if (cpp)
 727                                 newae(cpp, arg);
 728                         else if (m4)
 729                                 newae(m4, arg);
 730                         else
 731                                 newae(as, arg);
 732                         break;
 733                 case 'I':
 734                         if (cpp)
 735                                 newae(cpp, arg);
 736                         else
 737                                 newae(as, arg);
 738                         break;
 739                 case '-':       /* a gas-specific option */
 740                         newae(as, arg);
 741                         break;
 742                 }
 743         }
 744 
 745 #if defined(AW_TARGET_i386)
 746         if (as64)
 747                 newae(as, "--64");
 748         else
 749                 newae(as, "--32");
 750 #endif
 751 
 752 
 753         /*
 754          * gas for 32-bit arm defaults to a much older version of the arm
 755          * architecture than we can really support. Because of that, we instead
 756          * opt to make sure that we set the minimum architecture to armv6k, the
 757          * minimum of what we actually support.
 758          */
 759 #if defined(AW_TARGET_arm)
 760         if (as64) {
 761                 return (error("no 64-bit aw target for arm"));
 762         } else {
 763                 newae(as, "-march=armv7-a");
 764                 newae(as, "-mfpu=vfpv3-d16");
 765                 newae(as, "-mfloat-abi=hard");
 766         }
 767 #endif
 768 
 769         if (srcfile == NULL)
 770                 return (usage("no source file(s) specified"));
 771         if (outfile == NULL)
 772                 outfile = "a.out";
 773         newae(as, "-o");
 774         newae(as, outfile);
 775 
 776         asargv = aeltoargv(as);
 777         if (cpp) {
 778 #if defined(AW_TARGET_sparc)
 779                 newae(cpp, "-Dsparc");
 780                 newae(cpp, "-D__sparc");
 781                 if (as64)
 782                         newae(cpp, "-D__sparcv9");
 783                 else
 784                         newae(cpp, "-D__sparcv8");
 785 #elif defined(AW_TARGET_i386)
 786                 if (as64) {
 787                         newae(cpp, "-D__x86_64");
 788                         newae(cpp, "-D__amd64");
 789                 } else {
 790                         newae(cpp, "-Di386");
 791                         newae(cpp, "-D__i386");
 792                 }
 793 #elif defined(AW_TARGET_arm)
 794                 newae(cpp, "-Darm");
 795                 newae(cpp, "-D__arm");
 796 #else
 797 #error  "need isa-dependent defines"
 798 #endif
 799                 code = pipeline(aeltoargv(cpp), asargv);
 800         } else if (m4)
 801                 code = pipeline(aeltoargv(m4), asargv);
 802         else {
 803                 /*
 804                  * XXX  should arrange to fork/exec so that we
 805                  *      can unlink the output file if errors are
 806                  *      detected..
 807                  */
 808                 (void) execvp(asargv[0], asargv);
 809                 perror("execvp");
 810                 (void) fprintf(stderr, "%s: couldn't run %s\n",
 811                     progname, asargv[0]);
 812                 code = 7;
 813         }
 814         if (code != 0)
 815                 (void) unlink(outfile);
 816         return (code);
 817 }