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-2013 Julian Seward
11      jseward@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
57static 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   /* Not that it's actually relevant, since we don't support PPC on
66      MacOS X, but .. the Apple PPC descriptors refer to the BE
67      variant, since the LE variant is something that appeared long
68      after Apple dropped PPC. */
69   { CPU_TYPE_POWERPC,   "ppc",    "ppc32" },
70   { CPU_TYPE_POWERPC64, "ppc64",  "ppc64be" }
71};
72static int valid_archs_count = sizeof(valid_archs)/sizeof(valid_archs[0]);
73
74static const char *name_for_cputype(cpu_type_t cputype)
75{
76   int i;
77   for (i = 0; i < valid_archs_count; i++) {
78      if (valid_archs[i].cputype == cputype) {
79         return valid_archs[i].valgrind_name;
80      }
81   }
82   return NULL;
83}
84
85/* Report fatal errors */
86__attribute__((noreturn))
87static void barf ( const char *format, ... )
88{
89   va_list vargs;
90
91   va_start(vargs, format);
92   fprintf(stderr, "valgrind: ");
93   vfprintf(stderr, format, vargs);
94   fprintf(stderr, "\n");
95   va_end(vargs);
96
97   exit(1);
98   /*NOTREACHED*/
99   assert(0);
100}
101
102/* Search the path for the client program */
103static const char *find_client(const char *clientname)
104{
105   static char fullname[PATH_MAX];
106   const char *path = getenv("PATH");
107   const char *colon;
108
109   while (path)
110   {
111      if ((colon = strchr(path, ':')) == NULL)
112      {
113         strcpy(fullname, path);
114         path = NULL;
115      }
116      else
117      {
118         memcpy(fullname, path, colon - path);
119         fullname[colon - path] = '\0';
120         path = colon + 1;
121      }
122
123      strcat(fullname, "/");
124      strcat(fullname, clientname);
125
126      if (access(fullname, R_OK|X_OK) == 0)
127         return fullname;
128   }
129
130   return clientname;
131}
132
133static int fat_has_cputype(struct fat_header *fh, cpu_type_t cputype)
134{
135   struct fat_arch *fa = (struct fat_arch *)(fh+1);
136   uint32_t nfat_arch = ntohl(fh->nfat_arch);
137   uint32_t i;
138   for (i = 0; i < nfat_arch; i++) {
139      if (ntohl(fa[i].cputype) == cputype) return 1;
140   }
141   return 0;
142}
143
144/* Examine the client and work out which arch it is for */
145static const char *select_arch(
146      const char *clientname, cpu_type_t default_cputype,
147      const char *default_arch)
148{
149   uint8_t buf[4096];
150   ssize_t bytes;
151   int fd = open(find_client(clientname), O_RDONLY);
152   if (fd < 0) {
153      barf("%s: %s", clientname, strerror(errno));
154   }
155
156   bytes = read(fd, buf, sizeof(buf));
157   close(fd);
158   if (bytes != sizeof(buf)) {
159      return NULL;
160   }
161
162   // If it's thin, return that arch.
163   {
164      struct mach_header *mh = (struct mach_header *)buf;
165      if (mh->magic == MH_MAGIC  ||  mh->magic == MH_MAGIC_64) {
166         return name_for_cputype(mh->cputype);
167      } else if (mh->magic == MH_CIGAM  ||  mh->magic == MH_CIGAM_64) {
168         return name_for_cputype(OSSwapInt32(mh->cputype));
169      }
170   }
171
172   // If it's fat, look for a good arch.
173   {
174      struct fat_header *fh = (struct fat_header *)buf;
175      if (ntohl(fh->magic) == FAT_MAGIC) {
176         uint32_t nfat_arch = ntohl(fh->nfat_arch);
177         int i;
178         // If only one fat arch, use it.
179         if (nfat_arch == 1) {
180            struct fat_arch *fa = (struct fat_arch *)(fh+1);
181            return name_for_cputype(ntohl(fa->cputype));
182         }
183         // Scan fat headers for default arch.
184         if (fat_has_cputype(fh, default_cputype)) {
185            return default_arch;
186         }
187
188         // Scan fat headers for any supported arch.
189         for (i = 0; i < valid_archs_count; i++) {
190            if (fat_has_cputype(fh, valid_archs[i].cputype)) {
191               return valid_archs[i].valgrind_name;
192            }
193         }
194      }
195   }
196
197   return NULL;
198}
199
200
201/* Where we expect to find all our aux files */
202static const char *valgrind_lib;
203
204int main(int argc, char** argv, char** envp)
205{
206   int i, j, loglevel;
207   const char *toolname = NULL;
208   const char *clientname = NULL;
209   int clientname_arg = 0;
210   const char *archname = NULL;
211   const char *arch;
212   const char *default_arch;
213   cpu_type_t default_cputype;
214   char *toolfile;
215   char launcher_name[PATH_MAX+1];
216   char* new_line;
217   char* set_cwd;
218   char* cwd;
219   char** new_env;
220   char **new_argv;
221   int new_argc;
222
223   /* Start the debugging-log system ASAP.  First find out how many
224      "-d"s were specified.  This is a pre-scan of the command line.
225      At the same time, look for the tool name. */
226   loglevel = 0;
227   for (i = 1; i < argc; i++) {
228      if (argv[i][0] != '-') {
229         clientname = argv[i];
230         clientname_arg = i;
231         break;
232      }
233      if (0 == strcmp(argv[i], "--")) {
234         if (i+1 < argc) {
235            clientname = argv[i+1];
236            clientname_arg = i;
237         }
238         break;
239      }
240      if (0 == strcmp(argv[i], "-d"))
241         loglevel++;
242      if (0 == strncmp(argv[i], "--tool=", 7))
243         toolname = argv[i] + 7;
244      if (0 == strncmp(argv[i], "--arch=", 7))
245         archname = argv[i] + 7;
246   }
247
248   /* ... and start the debug logger.  Now we can safely emit logging
249      messages all through startup. */
250   VG_(debugLog_startup)(loglevel, "Stage 1");
251
252   /* Make sure we know which tool we're using */
253   if (toolname) {
254      VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname);
255   } else {
256      VG_(debugLog)(1, "launcher",
257                       "no tool requested, defaulting to 'memcheck'\n");
258      toolname = "memcheck";
259   }
260
261   /* Find the real executable if clientname is an app bundle. */
262   if (clientname) {
263      struct stat st;
264      if (0 == stat(clientname, &st)  &&  (st.st_mode & S_IFDIR)) {
265         char *copy = strdup(clientname);
266         char *appname = basename(copy);
267         char *dot = strrchr(appname, '.');
268         if (dot) {
269            char *newclient;
270            *dot = '\0';
271            asprintf(&newclient, "%s/Contents/MacOS/%s", clientname, appname);
272            VG_(debugLog)(1, "launcher", "Using executable in app bundle: %s\n", newclient);
273            clientname = newclient;
274            argv[clientname_arg] = newclient;
275         }
276         free(copy);
277      }
278   }
279
280   /* Establish the correct VALGRIND_LIB. */
281   {  const char *cp;
282      cp = getenv(VALGRIND_LIB);
283      valgrind_lib = ( cp == NULL ? VG_LIBDIR : cp );
284      VG_(debugLog)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib);
285   }
286
287   /* Find installed architectures. Use vgpreload_core-<platform>.so as the
288    * indicator of whether the platform is installed. */
289   for (i = 0; i < valid_archs_count; i++) {
290      char *vgpreload_core;
291      asprintf(&vgpreload_core, "%s/vgpreload_core-%s-darwin.so", valgrind_lib, valid_archs[i].valgrind_name);
292      if (access(vgpreload_core, R_OK|X_OK) != 0) {
293         VG_(debugLog)(1, "launcher", "arch '%s' IS NOT installed\n", valid_archs[i].valgrind_name);
294         memset(&valid_archs[i], 0, sizeof(valid_archs[i]));
295      } else {
296         VG_(debugLog)(1, "launcher", "arch '%s' IS installed\n", valid_archs[i].valgrind_name);
297      }
298      free(vgpreload_core);
299   }
300
301   /* Find the "default" arch (VGCONF_ARCH_PRI from configure).
302      This is the preferred arch from fat files and the fallback. */
303   default_arch = NULL;
304   default_cputype = 0;
305   for (i = 0; i < valid_archs_count; i++) {
306      if (!valid_archs[i].cputype) continue;
307      if (0 == strncmp(VG_PLATFORM, valid_archs[i].valgrind_name,
308                       strlen(valid_archs[i].valgrind_name)))
309      {
310         default_arch = valid_archs[i].valgrind_name;
311         default_cputype = valid_archs[i].cputype;
312         break;
313      }
314   }
315   if (i == valid_archs_count) barf("Unknown/uninstalled VG_PLATFORM '%s'", VG_PLATFORM);
316   assert(NULL != default_arch);
317   assert(0 != default_cputype);
318
319   /* Work out what arch to use, or use the default arch if not possible. */
320   if (archname != NULL) {
321      // --arch from command line
322      arch = NULL;
323      for (i = 0; i < valid_archs_count; i++) {
324         if (0 == strcmp(archname, valid_archs[i].apple_name)  ||
325             0 == strcmp(archname, valid_archs[i].valgrind_name))
326         {
327            arch = valid_archs[i].valgrind_name;
328            break;
329         }
330      }
331      if (i == valid_archs_count) barf("Unknown --arch '%s'", archname);
332      assert(NULL != arch);
333      VG_(debugLog)(1, "launcher", "using arch '%s' from --arch=%s\n",
334                    arch, archname);
335   }
336   else if (clientname == NULL) {
337      // no client executable; use default as fallback
338      VG_(debugLog)(1, "launcher",
339                       "no client specified, defaulting arch to '%s'\n",
340                        default_arch);
341      arch = default_arch;
342   }
343   else if ((arch = select_arch(clientname, default_cputype,default_arch))) {
344      // arch from client executable
345      VG_(debugLog)(1, "launcher", "selected arch '%s'\n", arch);
346   }
347   else {
348      // nothing found in client executable; use default as fallback
349      VG_(debugLog)(1, "launcher",
350                       "no arch detected, defaulting arch to '%s'\n",
351                       default_arch);
352      arch = default_arch;
353   }
354
355   cwd = getcwd(NULL, 0);
356   if (!cwd) barf("Current directory no longer exists.");
357
358   /* Figure out the name of this executable (viz, the launcher), so
359      we can tell stage2.  stage2 will use the name for recursive
360      invokations of valgrind on child processes. */
361   memset(launcher_name, 0, PATH_MAX+1);
362   for (i = 0; envp[i]; i++)
363       ; /* executable path is after last envp item */
364   /* envp[i] == NULL ; envp[i+1] == executable_path */
365   if (envp[i+1][0] != '/') {
366      strcpy(launcher_name, cwd);
367      strcat(launcher_name, "/");
368   }
369   if (strlen(launcher_name) + strlen(envp[i+1]) > PATH_MAX)
370      barf("launcher path is too long");
371   strcat(launcher_name, envp[i+1]);
372   VG_(debugLog)(1, "launcher", "launcher_name = %s\n", launcher_name);
373
374   /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */
375   asprintf(&new_line, VALGRIND_LAUNCHER "=%s", launcher_name);
376
377   /* tediously augment the env: VALGRIND_STARTUP_PWD_%PID_XYZZY=current_working_dir */
378   asprintf(&set_cwd, "VALGRIND_STARTUP_PWD_%u_XYZZY=%s", getppid(), cwd);
379
380   // Note that Apple binaries get a secret fourth arg, "char* apple", which
381   // contains the executable path.  Don't forget about it.
382   for (j = 0; envp[j]; j++)
383      ;
384   new_env = malloc((j+4) * sizeof(char*));
385   if (new_env == NULL)
386      barf("malloc of new_env failed.");
387   for (i = 0; i < j; i++)
388      new_env[i] = envp[i];
389   new_env[i++] = new_line;
390   new_env[i++] = set_cwd;
391   new_env[i++] = NULL;
392   new_env[i  ] = envp[i-2]; // the 'apple' arg == the executable_path
393   assert(i == j+3);
394
395   /* tediously edit env: hide dyld options from valgrind's captive dyld */
396   for (i = 0; envp[i]; i++) {
397      if (0 == strncmp(envp[i], "DYLD_", 5)) {
398         envp[i][0] = 'V';  /* VYLD_; changed back by initimg-darwin */
399      }
400   }
401
402   /* tediously edit argv: remove --arch= */
403   new_argv = malloc((1+argc) * sizeof(char *));
404   for (i = 0, new_argc = 0; i < argc; i++) {
405      if (0 == strncmp(argv[i], "--arch=", 7)) {
406         // skip
407      } else {
408         new_argv[new_argc++] = argv[i];
409      }
410   }
411   new_argv[new_argc++] = NULL;
412
413   /* Build the stage2 invokation, and execve it.  Bye! */
414   asprintf(&toolfile, "%s/%s-%s-darwin", valgrind_lib, toolname, arch);
415   if (access(toolfile, R_OK|X_OK) != 0) {
416      barf("tool '%s' not installed (%s) (%s)", toolname, toolfile, strerror(errno));
417   }
418
419   VG_(debugLog)(1, "launcher", "launching %s\n", toolfile);
420
421   execve(toolfile, new_argv, new_env);
422
423   fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s-darwin': %s\n",
424                   toolname, arch, strerror(errno));
425
426   exit(1);
427}
428