changeset 1656:00928b6d7895

First pass at ccwrap rewrite for (among other things) musl support.
author Rob Landley <rob@landley.net>
date Thu, 19 Jun 2014 05:58:51 -0500
parents e74afb9aac72
children 84e47eac0f28
files sources/toys/ccwrap2.c
diffstat 1 files changed, 442 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sources/toys/ccwrap2.c	Thu Jun 19 05:58:51 2014 -0500
@@ -0,0 +1,442 @@
+/* Copyright 2013 Rob Landley <rob@landley.net>
+ *
+ * C compiler wrapper. Parses command line, supplies path information for
+ * headers and libraries.
+ */
+
+#undef _FORTIFY_SOURCE
+
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Default to musl
+#ifndef DYNAMIC_LINKER
+#define DYNAMIC_LINKER "/lib/libc.so"
+#endif
+
+// Some plumbing from toybox
+
+void *xmalloc(long len)
+{
+  void *ret = malloc(len);
+
+  if (!ret) {
+    fprintf(stderr, "bad malloc\n");
+    exit(1);
+  }
+}
+
+// Die unless we can allocate enough space to sprintf() into.
+char *xmprintf(char *format, ...)
+{
+  va_list va, va2;
+  int len;
+  char *ret;
+
+  va_start(va, format);
+  va_copy(va2, va);
+
+  // How long is it?
+  len = vsnprintf(0, 0, format, va);
+  len++;
+  va_end(va);
+
+  // Allocate and do the sprintf()
+  ret = xmalloc(len);
+  vsnprintf(ret, len, format, va2);
+  va_end(va2);
+
+  return ret;
+}
+
+// Confirm that a regular file exists, and (optionally) has the executable bit.
+int is_file(char *filename, int has_exe)
+{
+  // Confirm it has the executable bit set, if necessary.
+  if (!has_exe || !access(filename, X_OK)) {
+    struct stat st;
+
+    // Confirm it exists and is not a directory.
+    if (!stat(filename, &st) && S_ISREG(st.st_mode)) return 1;
+  }
+  return 0;
+}
+
+// Find a file in a colon-separated path
+char *find_in_path(char *path, char *filename, int has_exe)
+{
+  char *cwd = getcwd(0, 0);
+
+  if (index(filename, '/') && is_file(filename, has_exe))
+    return realpath(filename, 0);
+
+  while (path) {
+    char *str, *next = path ? index(path, ':') : 0;
+    int len = next ? next-path : strlen(path);
+
+    if (!len) str = xmprintf("%s/%s", cwd, filename);
+    else str = xmprintf("%*s/%s", len, path, filename);
+
+    // If it's not a directory, return it.
+    if (is_file(str, has_exe)) {
+      char *s = realpath(str, 0);
+
+      free(str);
+      free(cwd);
+
+      return s;
+    } else free(str);
+
+    if (next) next++;
+    path = next;
+  }
+  free(cwd);
+
+  return NULL;
+}
+
+struct dlist {
+  struct dlist *next, *prev;
+  char *str;
+};
+
+// Append to end of doubly linked list (in-order insertion)
+void dlist_add(struct dlist **list, char *str)
+{
+  struct dlist *new = xmalloc(sizeof(struct dlist));
+
+  if (*list) {
+    new->next = *list;
+    new->prev = (*list)->prev;
+    (*list)->prev->next = new;
+    (*list)->prev = new;
+  } else *list = new->next = new->prev = new;
+
+  *list = new;
+}
+
+// Some compiler versions don't provide separate T and S versions of begin/end,
+// so fall back to the base version if they're not there.
+
+char *find_TSpath(char *base, char *top, int use_shared, int use_static_linking)
+{
+  int i;
+  char *temp;
+
+  temp = xmprintf(base, top,
+    use_shared ? "S.o" : use_static_linking ? "T.o" : ".o");
+
+  if (!is_file(temp, 0)) {
+    free(temp);
+    temp = xmprintf(base, top, ".o");
+  }
+
+  return temp;
+}
+
+
+enum {
+  Clibccso, Clink, Cprofile, Cshared, Cstart, Cstatic, Cstdinc, Cstdlib,
+  Cverbose, Cx, Cdashdash,
+
+  CPctordtor, CP, CPstdinc
+};
+
+#define MASK_BIT(X) (1<<X)
+#define SET_FLAG(X) flags |= MASK_BIT(X)
+#define CLEAR_FLAG(X) flags &= MASK_BIT(X)
+#define GET_FLAG(X) flags & MASK_BIT(X)
+
+// Read the command line arguments and work out status
+int main(int argc, char *argv[])
+{
+  char *topdir, *ccprefix, *dynlink, *cc, *temp, **keepv, **hdr, **outv;
+  int i, keepc, srcfiles, flags, outc;
+  struct dlist *libs = 0;
+
+  keepv = xmalloc(argc*sizeof(char *));
+  flags = MASK_BIT(Clink)|MASK_BIT(Cstart)|MASK_BIT(Cstdinc)|MASK_BIT(Cstdlib)
+          |MASK_BIT(CPctordtor);
+  keepc = srcfiles = 0;
+
+  if (getenv("CCWRAP_DEBUG")) {
+    SET_FLAG(Cverbose);
+    fprintf(stderr, "incoming: ");
+    for (i=0; i<argc; i++) fprintf(stderr, "%s ", argv[i]);
+    fprintf(stderr, "\n\n");
+  }
+
+  // Find the cannonical path to the directory containing our executable
+  topdir = find_in_path(getenv("PATH"), *argv, 1);
+  if (!topdir || !(temp = rindex(topdir, '/'))) {
+    fprintf(stderr, "Can't find %s in $PATH (did you export it?)\n", *argv);
+    exit(1);
+  }
+  // strip off bin/ before filename
+  temp -= 4;
+  if (temp-topdir<0 || strncmp("/bin", temp, 4)) {
+    fprintf(stderr, "Executable not in bin directory!?\n");
+    exit(1);
+  }
+  *temp = 0;
+
+  // Add our binary's directory and the tools directory to $PATH so gcc's
+  // convulsive flailing probably blunders through here first.
+  // Note: do this before reading topdir from environment variable, because
+  // toolchain binaries go with wrapper even if headers/libraries don't.
+  temp = getenv("PATH");
+  if (!temp) temp = "";
+  temp = xmprintf("PATH=%s/bin:%s/tools/bin:%s", topdir, topdir, temp);
+  putenv(temp);
+
+  // Override header/library search path with environment variable?
+  temp = getenv("CCWRAP_TOPDIR");
+  if (!temp) {
+    cc = xmprintf("%sCCWRAP_TOPDIR", ccprefix);
+
+    for (i=0; cc[i]; i++) if (cc[i] == '-') cc[i]='_';
+    temp = getenv(cc);
+    free(cc);
+  }
+  if (temp) {
+    free(topdir);
+    topdir = temp;
+  }
+ 
+  // Name of the C compiler we're wrapping.
+  cc = getenv("CCWRAP_CC");
+  if (!cc) cc = "rawcc";
+ 
+  // figure out cross compiler prefix
+  i = strlen(ccprefix = basename(*argv));
+  if (i<2) {
+    fprintf(stderr, "Bad name '%s'\n", ccprefix);
+    exit(1);
+  }
+  if (!strcmp("++", ccprefix+i-2)) {
+    cc = xmprintf("%s++", cc);
+    SET_FLAG(CP);
+    SET_FLAG(CPstdinc);
+  }
+  if (!strcmp("gcc", ccprefix+i-3)) i -= 3;   // TODO: yank
+  else if (!strcmp("cc", ccprefix+i-2)) i-=2;
+  else if (!strcmp("cpp", ccprefix+i-3)) {
+    i -= 3;
+    CLEAR_FLAG(Clink);
+  } else return 1; // TODO: wrap ld
+  if (!(ccprefix = strndup(ccprefix, i))) exit(1);
+
+  // Does toolchain have a shared libcc?
+  temp = xmprintf("%s/lib/libgcc_s.so", topdir);
+  if (is_file(temp, 0)) SET_FLAG(Clibccso);
+  free(temp);
+
+  // Where's the dynamic linker?
+  temp = getenv("CCWRAP_DYNAMIC_LINKER");
+  if (!temp) temp = DYNAMIC_LINKER;
+  dynlink = xmprintf("-Wl,--dynamic-linker,%s", temp);
+
+  // Fallback library search path, these will wind up at the end
+  dlist_add(&libs, xmprintf("%s/lib", topdir));
+  dlist_add(&libs, xmprintf("%s/cc/lib", topdir));
+
+  // Parse command line arguments
+  for (i=0; i<argc; i++) {
+    char *c = keepv[keepc++] = argv[i];
+
+    if (!strcmp(c, "--")) SET_FLAG(Cdashdash);
+
+    // is this an option?
+    if (*c == '-' && !GET_FLAG(Cdashdash)) c++;
+    else {
+      srcfiles++;
+      continue;
+    }
+
+    // Second dash?
+    if (*c == '-') {
+      // Passthrough double dash versions of single-dash options.
+      if (!strncmp(c, "-print-", 7) || !strncmp(c, "-static", 7)
+          || !strncmp(c, "-shared", 7)) c++;
+      else if (!strcmp(c, "-no-ctors")) {
+        CLEAR_FLAG(CPctordtor);
+        keepc--;
+      }
+      continue;
+    }
+
+    // -M and -MM imply -E and thus no linking.
+    // Other -M? options don't, including -MMD
+    if (*c == 'M' && c[1] && (c[1] != 'M' || c[2])) continue;
+
+    // compile, preprocess, assemble... options that supporess linking.
+    if (strchr("cEMS", *c))  CLEAR_FLAG(Clink);
+    else if (*c == 'L') {
+       if (c[1]) dlist_add(&libs, c+1);
+       else if (!argv[++i]) {
+         fprintf(stderr, "-L at end of args\n");
+         exit(1);
+       } else dlist_add(&libs, argv[i]);
+       keepc--;
+    } else if (*c == 'f') {
+      if (!strcmp(c, "fprofile-arcs")) SET_FLAG(Cprofile);
+    } else if (*c == 'n') {
+      keepc--;
+      if (!strcmp(c, "nodefaultlibs")) CLEAR_FLAG(Cstdlib);
+      else if (!strcmp(c, "nostartfiles")) {
+        CLEAR_FLAG(CPctordtor);
+        CLEAR_FLAG(Cstart);
+      } else if (!strcmp(c, "nostdinc")) CLEAR_FLAG(Cstdinc);
+      else if (!strcmp(c, "nostdinc++")) CLEAR_FLAG(CPstdinc);
+      else if (!strcmp(c, "nostdlib")) {
+        CLEAR_FLAG(Cstdlib);
+        CLEAR_FLAG(Cstart);
+        CLEAR_FLAG(CPctordtor);
+      } else keepc++;
+    } else if (*c == 'p') {
+      if (!strncmp(c, "print-", 6)) {
+        struct dlist *dl;
+        int show = 0;
+
+        // Just add prefix to prog-name
+        if (!strncmp(c += 6, "prog-name=", 10)) {
+          printf("%s%s", ccprefix, c+10);
+          exit(0);
+        }
+
+        if (!strncmp(c, "file-name=", 10)) c += 10;
+        else if (!strcmp(c, "search-dirs")) {
+          c = "";
+          show = 1;
+          printf("install: %s/\nprograms: %s\nlibraries:",
+                 topdir, getenv("PATH"));
+        } else if (!strcmp(c, "libgcc-file-name")) c = "libgcc.a";
+        else break;
+
+        // Adjust dlist before traversing (move fallback to end, break circle)
+        libs = libs->next->next;
+        libs->prev->next = 0;
+
+        // Either display the list, or find first hit.
+        for (dl = libs; dl; dl = dl->next) {
+          if (show) printf(":%s" + (dl==libs), dl->str);
+          else if (!access(dl->str, F_OK)) break;
+        }
+        if (dl) printf("%s", dl->str);
+        printf("\n");
+
+        return 0;
+      } else if (!strcmp(c, "pg")) SET_FLAG(Cprofile);
+    } else if (*c == 's') {
+      keepc--;
+      if (!strcmp(c, "shared")) {
+        CLEAR_FLAG(Cstart);
+        SET_FLAG(Cshared);
+      } else if (!strcmp(c, "static")) {
+        SET_FLAG(Cstatic);
+        CLEAR_FLAG(Clibccso);
+      } else if (!strcmp(c, "shared-libgcc")) SET_FLAG(Clibccso);
+      else if (!strcmp(c, "static-libgcc")) CLEAR_FLAG(Clibccso);
+      else keepc++;
+    } else if (*c == 'v' && !c[1]) {
+      SET_FLAG(Cverbose);
+      printf("%s: %s\n", argv[0], topdir);
+    } else if (!strncmp(c, "Wl,", 3)) {
+      temp = strstr(c, ",-static");
+      if (temp && (!temp[8] || temp[8]==',')) {
+        SET_FLAG(Cstatic);
+        CLEAR_FLAG(Clibccso);
+      }
+      // This argument specifies dynamic linker, so we shouldn't.
+      if (strstr(c, "--dynamic-linker")) dynlink = 0;
+    } else if (*c == 'x') SET_FLAG(Cx);
+  }
+
+  // Initialize argument list for exec call
+
+// what's a good outc size?
+
+  outc = (argc+keepc+32)*sizeof(char *);
+  memset(outv = xmalloc(outc), 0, outc);
+  outc = 0;
+  outv[outc++] = cc;
+
+  // Are we linking?
+  if (srcfiles) {
+    outv[outc++] = "-nostdinc";
+    outv[outc++] = "-nostdlib";
+    if (GET_FLAG(CP)) {
+      outv[outc++] = "-nostdinc++";
+      if (GET_FLAG(CPstdinc)) {
+        outv[outc++] = "-isystem";
+        outv[outc++] = xmprintf("%s/c++/include", topdir);
+      }
+    }
+    if (GET_FLAG(Cstdinc)) {
+      outv[outc++] = "-isystem";
+      outv[outc++] = xmprintf("%s/include", topdir);
+      outv[outc++] = "-isystem";
+      outv[outc++] = xmprintf("%s/cc/include", topdir);
+    }
+    if (GET_FLAG(Clink)) {
+      // Zab defaults, add dynamic linker
+      outv[outc++] = GET_FLAG(Cstatic) ? "-static" : dynlink;
+      // Copy libraries to output (first move fallback to end, break circle)
+      libs = libs->next->next;
+      libs->prev->next = 0;
+      while (libs) outv[outc++] = xmprintf("-L%s", libs->str);
+      if (GET_FLAG(Cstdlib))
+        outv[outc++] = xmprintf("-Wl,-rpath-link,%s/lib", topdir); // TODO: in?
+
+      // TODO: -fprofile-arcs
+      if (GET_FLAG(Cprofile)) xmprintf("%s/lib/gcrt1.o", topdir);
+      if (GET_FLAG(CPctordtor)) {
+        outv[outc++] = xmprintf("%s/lib/crti.o", topdir);
+        outv[outc++] = find_TSpath("%s/cc/lib/crtbegin%s", topdir,
+                                   GET_FLAG(Cshared), GET_FLAG(Cstatic));
+      }
+      if (!GET_FLAG(Cprofile) && GET_FLAG(Cstart))
+        outv[outc++] = xmprintf("%s/lib/%scrt1.o", topdir,
+                                GETFLAG(Cshared) ? "S" : "");
+    }
+  }
+
+  // Copy unclaimed arguments
+  memcpy(outv+outc, keepv, keepc*sizeof(char *));
+  outv += outc;
+
+  if (srcfiles && GET_FLAG(Clink)) {
+    if (GET_FLAG(Cx)) outv[outc++] = "-xnone";
+    if (GET_FLAG(Cstdlib)) {
+      if (GET_FLAG(CP)) {
+        outv[outc++] = "-lstdc++";
+        //outv[outc++] = "-lm";
+      }
+
+      // libgcc can call libc which can call libgcc
+      outv[outc++] = "-Wl,--start-group,--as-needed";
+      outv[outc++] = "-lgcc";
+      if (GET_FLAG(Clibccso)) outv[outc++] = "-lgcc_s";
+      else outv[outc++] = "-lgcc_eh";
+      outv[outc++] = "-lc";
+      outv[outc++] = "-Wl,--no-as-needed,--end-group";
+    }
+    if (GET_FLAG(CPctordtor)) {
+      outv[outc++] = find_TSpath("%s/cc/lib/crtend%s", topdir,
+                                 GET_FLAG(Cshared), GET_FLAG(Cstatic));
+      outv[outc++] = xmprintf("%s/lib/crtn.o", topdir);
+    }
+  }
+  outv[outc++] = 0;
+
+for(i=0; i<outc; i++) printf("\"%s\"", outv[i]);
+printf("\n");
+
+  return 0;
+}