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 (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Wrapper for the GNU assembler to make it accept the Sun assembler
  29  * arguments where possible.
  30  *
  31  * There are several limitations; the Sun assembler takes multiple
  32  * source files, we only take one.
  33  *
  34  * -b, -s, -xF, -T plain not supported.
  35  * -S isn't supported either, because while GNU as does generate
  36  * listings with -a, there's no obvious mapping between sub-options.
  37  * -K pic, -K PIC not supported either, though it's not clear what
  38  * these actually do ..
  39  * -Qy (not supported) adds a string to the .comment section
  40  * describing the assembler version, while
  41  * -Qn (supported) suppresses the string (also the default).
  42  *
  43  * We also add '-#' support to see invocation lines..
  44  * We also add '-xarch=amd64' in case we need to feed the assembler
  45  * something different (or in case we need to invoke a different binary
  46  * altogether!)
  47  */
  48 
  49 #include <sys/types.h>
  50 #include <sys/wait.h>
  51 #include <stdio.h>
  52 #include <unistd.h>
  53 #include <string.h>
  54 #include <stdlib.h>
  55 #include <sys/param.h>
  56 
  57 static const char *progname;
  58 static int verbose;
  59 
  60 struct aelist {
  61         int ael_argc;
  62         struct ae {
  63                 struct ae *ae_next;
  64                 char *ae_arg;
  65         } *ael_head, *ael_tail;
  66 };
  67 
  68 static struct aelist *
  69 newael(void)
  70 {
  71         return (calloc(sizeof (struct aelist), 1));
  72 }
  73 
  74 static void
  75 newae(struct aelist *ael, const char *arg)
  76 {
  77         struct ae *ae;
  78 
  79         ae = calloc(sizeof (*ae), 1);
  80         ae->ae_arg = strdup(arg);
  81         if (ael->ael_tail == NULL)
  82                 ael->ael_head = ae;
  83         else
  84                 ael->ael_tail->ae_next = ae;
  85         ael->ael_tail = ae;
  86         ael->ael_argc++;
  87 }
  88 
  89 static void
  90 fixae_arg(struct ae *ae, const char *newarg)
  91 {
  92         free(ae->ae_arg);
  93         ae->ae_arg = strdup(newarg);
  94 }
  95 
  96 static char **
  97 aeltoargv(struct aelist *ael)
  98 {
  99         struct ae *ae;
 100         char **argv;
 101         int argc;
 102 
 103         argv = calloc(sizeof (*argv), ael->ael_argc + 1);
 104 
 105         for (argc = 0, ae = ael->ael_head; ae; ae = ae->ae_next, argc++) {
 106                 argv[argc] = ae->ae_arg;
 107                 if (ae == ael->ael_tail)
 108                         break;
 109         }
 110 
 111         return (argv);
 112 }
 113 
 114 static int
 115 error(const char *arg)
 116 {
 117         (void) fprintf(stderr,
 118             "%s: as->gas mapping failed at or near arg '%s'\n", progname, arg);
 119         return (2);
 120 }
 121 
 122 static int
 123 usage(const char *arg)
 124 {
 125         if (arg != NULL)
 126                 (void) fprintf(stderr, "error: %s\n", arg);
 127         (void) fprintf(stderr, "Usage: %s [-V] [-#]\n"
 128             "\t[-xarch=architecture]\n"
 129             "\t[-o objfile] [-L]\n"
 130             "\t[-P [[-Ipath] [-Dname] [-Dname=def] [-Uname]]...]\n"
 131             "\t[-m] [-n] file.s ...\n", progname);
 132         return (3);
 133 }
 134 
 135 static void
 136 copyuntil(FILE *in, FILE *out, int termchar)
 137 {
 138         int c;
 139 
 140         while ((c = fgetc(in)) != EOF) {
 141                 if (out && fputc(c, out) == EOF)
 142                         exit(1);
 143                 if (c == termchar)
 144                         break;
 145         }
 146 }
 147 
 148 /*
 149  * Variant of copyuntil(), used for copying the path used
 150  * for .file directives. This version removes the workspace
 151  * from the head of the path, or failing that, attempts to remove
 152  * /usr/include. This is a workaround for the way gas handles
 153  * these directives. The objects produced by gas contain STT_FILE
 154  * symbols for every .file directive. These FILE symbols contain our
 155  * workspace paths, leading to wsdiff incorrectly flagging them as
 156  * having changed. By clipping off the workspace from these paths,
 157  * we eliminate these false positives.
 158  */
 159 static void
 160 copyuntil_path(FILE *in, FILE *out, int termchar,
 161     const char *wspace, size_t wspace_len)
 162 {
 163 #define PROTO_INC "/proto/root_i386/usr/include/"
 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         if ((cmd = getenv("AW_AS")) != NULL)
 507                 strlcpy(as_pgm, cmd, sizeof (as_pgm));
 508         else {
 509                 if ((dir = getenv("AW_AS_DIR")) == NULL)
 510                         dir = DEFAULT_AS_DIR;   /* /usr/sfw/bin */
 511                 (void) snprintf(as_pgm, sizeof (as_pgm), "%s/gas", dir);
 512         }
 513 
 514         if ((cmd = getenv("AW_AS64")) != NULL)
 515                 strlcpy(as64_pgm, cmd, sizeof (as64_pgm));
 516         else {
 517                 if ((dir = getenv("AW_AS64_DIR")) == NULL)
 518                         dir = DEFAULT_AS64_DIR; /* /usr/sfw/bin */
 519                 (void) snprintf(as64_pgm, sizeof (as_pgm), "%s/gas", dir);
 520         }
 521 
 522         if ((cmd = getenv("AW_M4")) != NULL)
 523                 strlcpy(m4_pgm, cmd, sizeof (m4_pgm));
 524         else {
 525                 if ((dir = getenv("AW_M4_DIR")) == NULL)
 526                         dir = DEFAULT_M4_DIR;   /* /usr/ccs/bin */
 527                 (void) snprintf(m4_pgm, sizeof (m4_pgm), "%s/m4", dir);
 528         }
 529 
 530         if ((cmd = getenv("AW_M4LIB")) != NULL)
 531                 strlcpy(m4_cmdefs, cmd, sizeof (m4_cmdefs));
 532         else {
 533                 if ((dir = getenv("AW_M4LIB_DIR")) == NULL)
 534                         dir = DEFAULT_M4LIB_DIR;        /* /usr/ccs/lib */
 535                 (void) snprintf(m4_cmdefs, sizeof (m4_cmdefs),
 536                     "%s/cm4defs", dir);
 537         }
 538 
 539         if ((cmd = getenv("AW_CPP")) != NULL)
 540                 strlcpy(cpp_pgm, cmd, sizeof (cpp_pgm));
 541         else {
 542                 if ((dir = getenv("AW_CPP_DIR")) == NULL)
 543                         dir = DEFAULT_CPP_DIR;  /* /usr/ccs/lib */
 544                 (void) snprintf(cpp_pgm, sizeof (cpp_pgm), "%s/cpp", dir);
 545         }
 546 
 547         newae(as, as_pgm);
 548         newae(as, "--warn");
 549         newae(as, "--fatal-warnings");
 550         newae(as, "--traditional-format");
 551 
 552         /*
 553          * Walk the argument list, translating as we go ..
 554          */
 555         while (--argc > 0) {
 556                 char *arg;
 557                 int arglen;
 558 
 559                 arg = *++argv;
 560                 arglen = strlen(arg);
 561 
 562                 if (*arg != '-') {
 563                         char *filename;
 564 
 565                         /*
 566                          * filenames ending in '.s' are taken to be
 567                          * assembler files, and provide the default
 568                          * basename of the output file.
 569                          *
 570                          * other files are passed through to the
 571                          * preprocessor, if present, or to gas if not.
 572                          */
 573                         filename = arg;
 574                         if (arglen > 2 &&
 575                             strcmp(arg + arglen - 2, ".s") == 0) {
 576                                 /*
 577                                  * Though 'as' allows multiple assembler
 578                                  * files to be processed in one invocation
 579                                  * of the assembler, ON only processes one
 580                                  * file at a time, which makes things a lot
 581                                  * simpler!
 582                                  */
 583                                 if (srcfile == NULL)
 584                                         srcfile = arg;
 585                                 else
 586                                         return (usage(
 587                                             "one assembler file at a time"));
 588 
 589                                 /*
 590                                  * If we haven't seen a -o option yet,
 591                                  * default the output to the basename
 592                                  * of the input, substituting a .o on the end
 593                                  */
 594                                 if (outfile == NULL) {
 595                                         char *argcopy;
 596 
 597                                         argcopy = strdup(arg);
 598                                         argcopy[arglen - 1] = 'o';
 599 
 600                                         if ((outfile = strrchr(
 601                                             argcopy, '/')) == NULL)
 602                                                 outfile = argcopy;
 603                                         else
 604                                                 outfile++;
 605                                 }
 606                         }
 607                         if (cpp)
 608                                 newae(cpp, filename);
 609                         else if (m4)
 610                                 newae(m4, filename);
 611                         else
 612                                 newae(as, filename);
 613                         continue;
 614                 } else
 615                         arglen--;
 616 
 617                 switch (arg[1]) {
 618                 case 'K':
 619                         /*
 620                          * -K pic
 621                          * -K PIC
 622                          */
 623                         if (arglen == 1) {
 624                                 if ((arg = *++argv) == NULL || *arg == '\0')
 625                                         return (usage("malformed -K"));
 626                                 argc--;
 627                         } else {
 628                                 arg += 2;
 629                         }
 630                         if (strcmp(arg, "PIC") != 0 && strcmp(arg, "pic") != 0)
 631                                 return (usage("malformed -K"));
 632                         break;          /* just ignore -Kpic for gcc */
 633                 case 'Q':
 634                         if (strcmp(arg, "-Qn") == 0)
 635                                 break;
 636                         /*FALLTHROUGH*/
 637                 case 'b':
 638                 case 's':
 639                 case 'T':
 640                         /*
 641                          * -b   Extra symbol table for source browser ..
 642                          *      not relevant to gas, thus should error.
 643                          * -s   Put stabs in .stabs section not stabs.excl
 644                          *      not clear if there's an equivalent
 645                          * -T   4.x migration option
 646                          */
 647                 default:
 648                         return (error(arg));
 649                 case 'x':
 650                         /*
 651                          * Accept -xarch special case to invoke alternate
 652                          * assemblers or assembler flags for different
 653                          * architectures.
 654                          */
 655                         if (strcmp(arg, "-xarch=amd64") == 0 ||
 656                             strcmp(arg, "-xarch=generic64") == 0) {
 657                                 as64++;
 658                                 fixae_arg(as->ael_head, as64_pgm);
 659                                 break;
 660                         }
 661                         /*
 662                          * XX64: Is this useful to gas?
 663                          */
 664                         if (strcmp(arg, "-xmodel=kernel") == 0)
 665                                 break;
 666 
 667                         /*
 668                          * -xF  Generates performance analysis data
 669                          *      no equivalent
 670                          */
 671                         return (error(arg));
 672                 case 'V':
 673                         newae(as, arg);
 674                         break;
 675                 case '#':
 676                         verbose++;
 677                         break;
 678                 case 'L':
 679                         newae(as, "--keep-locals");
 680                         break;
 681                 case 'n':
 682                         newae(as, "--no-warn");
 683                         break;
 684                 case 'o':
 685                         if (arglen != 1)
 686                                 return (usage("bad -o flag"));
 687                         if ((arg = *++argv) == NULL || *arg == '\0')
 688                                 return (usage("bad -o flag"));
 689                         outfile = arg;
 690                         argc--;
 691                         arglen = strlen(arg + 1);
 692                         break;
 693                 case 'm':
 694                         if (cpp)
 695                                 return (usage("-m conflicts with -P"));
 696                         if (m4 == NULL) {
 697                                 m4 = newael();
 698                                 newae(m4, m4_pgm);
 699                                 newae(m4, m4_cmdefs);
 700                         }
 701                         break;
 702                 case 'P':
 703                         if (m4)
 704                                 return (usage("-P conflicts with -m"));
 705                         if (cpp == NULL) {
 706                                 cpp = newael();
 707                                 newae(cpp, cpp_pgm);
 708                                 newae(cpp, "-D__GNUC_AS__");
 709                         }
 710                         break;
 711                 case 'D':
 712                 case 'U':
 713                         if (cpp)
 714                                 newae(cpp, arg);
 715                         else if (m4)
 716                                 newae(m4, arg);
 717                         else
 718                                 newae(as, arg);
 719                         break;
 720                 case 'I':
 721                         if (cpp)
 722                                 newae(cpp, arg);
 723                         else
 724                                 newae(as, arg);
 725                         break;
 726                 case '-':       /* a gas-specific option */
 727                         newae(as, arg);
 728                         break;
 729                 }
 730         }
 731 
 732 #if defined(__i386)
 733         if (as64)
 734                 newae(as, "--64");
 735         else
 736                 newae(as, "--32");
 737 #endif
 738 
 739         if (srcfile == NULL)
 740                 return (usage("no source file(s) specified"));
 741         if (outfile == NULL)
 742                 outfile = "a.out";
 743         newae(as, "-o");
 744         newae(as, outfile);
 745 
 746         asargv = aeltoargv(as);
 747         if (cpp) {
 748 #if defined(__sparc)
 749                 newae(cpp, "-Dsparc");
 750                 newae(cpp, "-D__sparc");
 751                 if (as64)
 752                         newae(cpp, "-D__sparcv9");
 753                 else
 754                         newae(cpp, "-D__sparcv8");
 755 #elif defined(__i386) || defined(__x86)
 756                 if (as64) {
 757                         newae(cpp, "-D__x86_64");
 758                         newae(cpp, "-D__amd64");
 759                 } else {
 760                         newae(cpp, "-Di386");
 761                         newae(cpp, "-D__i386");
 762                 }
 763 #else
 764 #error  "need isa-dependent defines"
 765 #endif
 766                 code = pipeline(aeltoargv(cpp), asargv);
 767         } else if (m4)
 768                 code = pipeline(aeltoargv(m4), asargv);
 769         else {
 770                 /*
 771                  * XXX  should arrange to fork/exec so that we
 772                  *      can unlink the output file if errors are
 773                  *      detected..
 774                  */
 775                 (void) execvp(asargv[0], asargv);
 776                 perror("execvp");
 777                 (void) fprintf(stderr, "%s: couldn't run %s\n",
 778                     progname, asargv[0]);
 779                 code = 7;
 780         }
 781         if (code != 0)
 782                 (void) unlink(outfile);
 783         return (code);
 784 }