Home | History | Annotate | Download | only in coregrind
      1 
      2 /*--------------------------------------------------------------------*/
      3 /*--- Launching valgrind                         launcher-darwin.c ---*/
      4 /*--------------------------------------------------------------------*/
      5 
      6 /*
      7    This file is part of Valgrind, a dynamic binary instrumentation
      8    framework.
      9 
     10    Copyright (C) 2000-2007 Julian Seward
     11       jseward (at) acm.org
     12 
     13    This program is free software; you can redistribute it and/or
     14    modify it under the terms of the GNU General Public License as
     15    published by the Free Software Foundation; either version 2 of the
     16    License, or (at your option) any later version.
     17 
     18    This program is distributed in the hope that it will be useful, but
     19    WITHOUT ANY WARRANTY; without even the implied warranty of
     20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     21    General Public License for more details.
     22 
     23    You should have received a copy of the GNU General Public License
     24    along with this program; if not, write to the Free Software
     25    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     26    02111-1307, USA.
     27 
     28    The GNU General Public License is contained in the file COPYING.
     29 */
     30 
     31 /* Note: this is a "normal" program and not part of Valgrind proper,
     32    and so it doesn't have to conform to Valgrind's arcane rules on
     33    no-glibc-usage etc. */
     34 
     35 #include <assert.h>
     36 #include <ctype.h>
     37 #include <errno.h>
     38 #include <fcntl.h>
     39 #include <libgen.h>
     40 #include <stdarg.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <sys/mman.h>
     45 #include <sys/param.h>
     46 #include <sys/stat.h>
     47 #include <sys/user.h>
     48 #include <unistd.h>
     49 #include <mach-o/fat.h>
     50 #include <mach-o/loader.h>
     51 
     52 #include "pub_core_debuglog.h"
     53 #include "pub_core_vki.h"       // Avoids warnings from pub_core_libcfile.h
     54 #include "pub_core_libcproc.h"  // For VALGRIND_LIB, VALGRIND_LAUNCHER
     55 #include "pub_core_ume.h"
     56 
     57 static struct {
     58    cpu_type_t cputype;
     59    const char *apple_name;     // e.g. x86_64
     60    const char *valgrind_name;  // e.g. amd64
     61 } valid_archs[] = {
     62    { CPU_TYPE_X86,       "i386",   "x86" },
     63    { CPU_TYPE_X86_64,    "x86_64", "amd64" },
     64    { CPU_TYPE_ARM,       "arm",    "arm" },
     65    { CPU_TYPE_POWERPC,   "ppc",    "ppc32" },
     66    { CPU_TYPE_POWERPC64, "ppc64",  "ppc64" },
     67 };
     68 static int valid_archs_count = sizeof(valid_archs)/sizeof(valid_archs[0]);
     69 
     70 static const char *name_for_cputype(cpu_type_t cputype)
     71 {
     72    int i;
     73    for (i = 0; i < valid_archs_count; i++) {
     74       if (valid_archs[i].cputype == cputype) {
     75          return valid_archs[i].valgrind_name;
     76       }
     77    }
     78    return NULL;
     79 }
     80 
     81 /* Report fatal errors */
     82 __attribute__((noreturn))
     83 static void barf ( const char *format, ... )
     84 {
     85    va_list vargs;
     86 
     87    va_start(vargs, format);
     88    fprintf(stderr, "valgrind: ");
     89    vfprintf(stderr, format, vargs);
     90    fprintf(stderr, "\n");
     91    va_end(vargs);
     92 
     93    exit(1);
     94    /*NOTREACHED*/
     95    assert(0);
     96 }
     97 
     98 /* Search the path for the client program */
     99 static const char *find_client(const char *clientname)
    100 {
    101    static char fullname[PATH_MAX];
    102    const char *path = getenv("PATH");
    103    const char *colon;
    104 
    105    while (path)
    106    {
    107       if ((colon = strchr(path, ':')) == NULL)
    108       {
    109          strcpy(fullname, path);
    110          path = NULL;
    111       }
    112       else
    113       {
    114          memcpy(fullname, path, colon - path);
    115          fullname[colon - path] = '\0';
    116          path = colon + 1;
    117       }
    118 
    119       strcat(fullname, "/");
    120       strcat(fullname, clientname);
    121 
    122       if (access(fullname, R_OK|X_OK) == 0)
    123          return fullname;
    124    }
    125 
    126    return clientname;
    127 }
    128 
    129 static int fat_has_cputype(struct fat_header *fh, cpu_type_t cputype)
    130 {
    131    struct fat_arch *fa = (struct fat_arch *)(fh+1);
    132    uint32_t nfat_arch = ntohl(fh->nfat_arch);
    133    uint32_t i;
    134    for (i = 0; i < nfat_arch; i++) {
    135       if (ntohl(fa[i].cputype) == cputype) return 1;
    136    }
    137    return 0;
    138 }
    139 
    140 /* Examine the client and work out which arch it is for */
    141 static const char *select_arch(
    142       const char *clientname, cpu_type_t default_cputype,
    143       const char *default_arch)
    144 {
    145    uint8_t buf[4096];
    146    ssize_t bytes;
    147    int fd = open(find_client(clientname), O_RDONLY);
    148    if (fd < 0) {
    149       barf("%s: %s", clientname, strerror(errno));
    150    }
    151 
    152    bytes = read(fd, buf, sizeof(buf));
    153    close(fd);
    154    if (bytes != sizeof(buf)) {
    155       return NULL;
    156    }
    157 
    158    // If it's thin, return that arch.
    159    {
    160       struct mach_header *mh = (struct mach_header *)buf;
    161       if (mh->magic == MH_MAGIC  ||  mh->magic == MH_MAGIC_64) {
    162          return name_for_cputype(mh->cputype);
    163       } else if (mh->magic == MH_CIGAM  ||  mh->magic == MH_CIGAM_64) {
    164          return name_for_cputype(OSSwapInt32(mh->cputype));
    165       }
    166    }
    167 
    168    // If it's fat, look for a good arch.
    169    {
    170       struct fat_header *fh = (struct fat_header *)buf;
    171       if (ntohl(fh->magic) == FAT_MAGIC) {
    172          uint32_t nfat_arch = ntohl(fh->nfat_arch);
    173          int i;
    174          // If only one fat arch, use it.
    175          if (nfat_arch == 1) {
    176             struct fat_arch *fa = (struct fat_arch *)(fh+1);
    177             return name_for_cputype(ntohl(fa->cputype));
    178          }
    179          // Scan fat headers for default arch.
    180          if (fat_has_cputype(fh, default_cputype)) {
    181             return default_arch;
    182          }
    183 
    184          // Scan fat headers for any supported arch.
    185          for (i = 0; i < valid_archs_count; i++) {
    186             if (fat_has_cputype(fh, valid_archs[i].cputype)) {
    187                return valid_archs[i].valgrind_name;
    188             }
    189          }
    190       }
    191    }
    192 
    193    return NULL;
    194 }
    195 
    196 
    197 /* Where we expect to find all our aux files */
    198 static const char *valgrind_lib;
    199 
    200 int main(int argc, char** argv, char** envp)
    201 {
    202    int i, j, loglevel;
    203    const char *toolname = NULL;
    204    const char *clientname = NULL;
    205    int clientname_arg = 0;
    206    const char *archname = NULL;
    207    const char *arch;
    208    const char *default_arch;
    209    cpu_type_t default_cputype;
    210    char *toolfile;
    211    char launcher_name[PATH_MAX+1];
    212    char* new_line;
    213    char* set_cwd;
    214    char* cwd;
    215    char** new_env;
    216    char **new_argv;
    217    int new_argc;
    218 
    219    /* Start the debugging-log system ASAP.  First find out how many
    220       "-d"s were specified.  This is a pre-scan of the command line.
    221       At the same time, look for the tool name. */
    222    loglevel = 0;
    223    for (i = 1; i < argc; i++) {
    224       if (argv[i][0] != '-') {
    225          clientname = argv[i];
    226          clientname_arg = i;
    227          break;
    228       }
    229       if (0 == strcmp(argv[i], "--")) {
    230          if (i+1 < argc) {
    231             clientname = argv[i+1];
    232             clientname_arg = i;
    233          }
    234          break;
    235       }
    236       if (0 == strcmp(argv[i], "-d"))
    237          loglevel++;
    238       if (0 == strncmp(argv[i], "--tool=", 7))
    239          toolname = argv[i] + 7;
    240       if (0 == strncmp(argv[i], "--arch=", 7))
    241          archname = argv[i] + 7;
    242    }
    243 
    244    /* ... and start the debug logger.  Now we can safely emit logging
    245       messages all through startup. */
    246    VG_(debugLog_startup)(loglevel, "Stage 1");
    247 
    248    /* Make sure we know which tool we're using */
    249    if (toolname) {
    250       VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname);
    251    } else {
    252       VG_(debugLog)(1, "launcher",
    253                        "no tool requested, defaulting to 'memcheck'\n");
    254       toolname = "memcheck";
    255    }
    256 
    257    /* Find the real executable if clientname is an app bundle. */
    258    if (clientname) {
    259       struct stat st;
    260       if (0 == stat(clientname, &st)  &&  (st.st_mode & S_IFDIR)) {
    261          char *copy = strdup(clientname);
    262          char *appname = basename(copy);
    263          char *dot = strrchr(appname, '.');
    264          if (dot) {
    265             char *newclient;
    266             *dot = '\0';
    267             asprintf(&newclient, "%s/Contents/MacOS/%s", clientname, appname);
    268             VG_(debugLog)(1, "launcher", "Using executable in app bundle: %s\n", newclient);
    269             clientname = newclient;
    270             argv[clientname_arg] = newclient;
    271          }
    272          free(copy);
    273       }
    274    }
    275 
    276    /* Establish the correct VALGRIND_LIB. */
    277    {  const char *cp;
    278       cp = getenv(VALGRIND_LIB);
    279       valgrind_lib = ( cp == NULL ? VG_LIBDIR : cp );
    280       VG_(debugLog)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib);
    281    }
    282 
    283    /* Find installed architectures. Use vgpreload_core-<platform>.so as the
    284     * indicator of whether the platform is installed. */
    285    for (i = 0; i < valid_archs_count; i++) {
    286       char *vgpreload_core;
    287       asprintf(&vgpreload_core, "%s/vgpreload_core-%s-darwin.so", valgrind_lib, valid_archs[i].valgrind_name);
    288       if (access(vgpreload_core, R_OK|X_OK) != 0) {
    289          VG_(debugLog)(1, "launcher", "arch '%s' IS NOT installed\n", valid_archs[i].valgrind_name);
    290          bzero(&valid_archs[i], sizeof(valid_archs[i]));
    291       } else {
    292          VG_(debugLog)(1, "launcher", "arch '%s' IS installed\n", valid_archs[i].valgrind_name);
    293       }
    294       free(vgpreload_core);
    295    }
    296 
    297    /* Find the "default" arch (VGCONF_ARCH_PRI from configure).
    298       This is the preferred arch from fat files and the fallback. */
    299    default_arch = NULL;
    300    default_cputype = 0;
    301    for (i = 0; i < valid_archs_count; i++) {
    302       if (!valid_archs[i].cputype) continue;
    303       if (0 == strncmp(VG_PLATFORM, valid_archs[i].valgrind_name,
    304                        strlen(valid_archs[i].valgrind_name)))
    305       {
    306          default_arch = valid_archs[i].valgrind_name;
    307          default_cputype = valid_archs[i].cputype;
    308          break;
    309       }
    310    }
    311    if (i == valid_archs_count) barf("Unknown/uninstalled VG_PLATFORM '%s'", VG_PLATFORM);
    312    assert(NULL != default_arch);
    313    assert(0 != default_cputype);
    314 
    315    /* Work out what arch to use, or use the default arch if not possible. */
    316    if (archname != NULL) {
    317       // --arch from command line
    318       arch = NULL;
    319       for (i = 0; i < valid_archs_count; i++) {
    320          if (0 == strcmp(archname, valid_archs[i].apple_name)  ||
    321              0 == strcmp(archname, valid_archs[i].valgrind_name))
    322          {
    323             arch = valid_archs[i].valgrind_name;
    324             break;
    325          }
    326       }
    327       if (i == valid_archs_count) barf("Unknown --arch '%s'", archname);
    328       assert(NULL != arch);
    329       VG_(debugLog)(1, "launcher", "using arch '%s' from --arch=%s\n",
    330                     arch, archname);
    331    }
    332    else if (clientname == NULL) {
    333       // no client executable; use default as fallback
    334       VG_(debugLog)(1, "launcher",
    335                        "no client specified, defaulting arch to '%s'\n",
    336                         default_arch);
    337       arch = default_arch;
    338    }
    339    else if ((arch = select_arch(clientname, default_cputype,default_arch))) {
    340       // arch from client executable
    341       VG_(debugLog)(1, "launcher", "selected arch '%s'\n", arch);
    342    }
    343    else {
    344       // nothing found in client executable; use default as fallback
    345       VG_(debugLog)(1, "launcher",
    346                        "no arch detected, defaulting arch to '%s'\n",
    347                        default_arch);
    348       arch = default_arch;
    349    }
    350 
    351    cwd = getcwd(NULL, 0);
    352    if (!cwd) barf("Current directory no longer exists.");
    353 
    354    /* Figure out the name of this executable (viz, the launcher), so
    355       we can tell stage2.  stage2 will use the name for recursive
    356       invokations of valgrind on child processes. */
    357    memset(launcher_name, 0, PATH_MAX+1);
    358    for (i = 0; envp[i]; i++)
    359        ; /* executable path is after last envp item */
    360    /* envp[i] == NULL ; envp[i+1] == executable_path */
    361    if (envp[i+1][0] != '/') {
    362       strcpy(launcher_name, cwd);
    363       strcat(launcher_name, "/");
    364    }
    365    if (strlen(launcher_name) + strlen(envp[i+1]) > PATH_MAX)
    366       barf("launcher path is too long");
    367    strcat(launcher_name, envp[i+1]);
    368    VG_(debugLog)(1, "launcher", "launcher_name = %s\n", launcher_name);
    369 
    370    /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */
    371    asprintf(&new_line, VALGRIND_LAUNCHER "=%s", launcher_name);
    372 
    373    /* tediously augment the env: VALGRIND_STARTUP_PWD_%PID_XYZZY=current_working_dir */
    374    asprintf(&set_cwd, "VALGRIND_STARTUP_PWD_%u_XYZZY=%s", getppid(), cwd);
    375 
    376    // Note that Apple binaries get a secret fourth arg, "char* apple", which
    377    // contains the executable path.  Don't forget about it.
    378    for (j = 0; envp[j]; j++)
    379       ;
    380    new_env = malloc((j+4) * sizeof(char*));
    381    if (new_env == NULL)
    382       barf("malloc of new_env failed.");
    383    for (i = 0; i < j; i++)
    384       new_env[i] = envp[i];
    385    new_env[i++] = new_line;
    386    new_env[i++] = set_cwd;
    387    new_env[i++] = NULL;
    388    new_env[i  ] = envp[i-2]; // the 'apple' arg == the executable_path
    389    assert(i == j+3);
    390 
    391    /* tediously edit env: hide dyld options from valgrind's captive dyld */
    392    for (i = 0; envp[i]; i++) {
    393       if (0 == strncmp(envp[i], "DYLD_", 5)) {
    394          envp[i][0] = 'V';  /* VYLD_; changed back by initimg-darwin */
    395       }
    396    }
    397 
    398    /* tediously edit argv: remove --arch= */
    399    new_argv = malloc((1+argc) * sizeof(char *));
    400    for (i = 0, new_argc = 0; i < argc; i++) {
    401       if (0 == strncmp(argv[i], "--arch=", 7)) {
    402          // skip
    403       } else {
    404          new_argv[new_argc++] = argv[i];
    405       }
    406    }
    407    new_argv[new_argc++] = NULL;
    408 
    409    /* Build the stage2 invokation, and execve it.  Bye! */
    410    asprintf(&toolfile, "%s/%s-%s-darwin", valgrind_lib, toolname, arch);
    411    if (access(toolfile, R_OK|X_OK) != 0) {
    412       barf("tool '%s' not installed (%s) (%s)", toolname, toolfile, strerror(errno));
    413    }
    414 
    415    VG_(debugLog)(1, "launcher", "launching %s\n", toolfile);
    416 
    417    execve(toolfile, new_argv, new_env);
    418 
    419    fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s-darwin': %s\n",
    420                    toolname, arch, strerror(errno));
    421 
    422    exit(1);
    423 }
    424