• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-2017 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 
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    /* 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 };
72 static int valid_archs_count = sizeof(valid_archs)/sizeof(valid_archs[0]);
73 
name_for_cputype(cpu_type_t cputype)74 static 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))
barf(const char * format,...)87 static 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 */
find_client(const char * clientname)103 static 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 
fat_has_cputype(struct fat_header * fh,cpu_type_t cputype)133 static 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 */
select_arch(const char * clientname,cpu_type_t default_cputype,const char * default_arch)145 static 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 */
202 static const char *valgrind_lib;
203 
main(int argc,char ** argv,char ** envp)204 int 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       invocations 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 invocation, 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