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-2011 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   { CPU_TYPE_POWERPC,   "ppc",    "ppc32" },
66   { CPU_TYPE_POWERPC64, "ppc64",  "ppc64" },
67};
68static int valid_archs_count = sizeof(valid_archs)/sizeof(valid_archs[0]);
69
70static 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))
83static 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 */
99static 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
129static 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 */
141static 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 */
198static const char *valgrind_lib;
199
200int 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