blob: 5097b58941645ef92c7a16d5f6f0d6d507ac07f9 [file] [log] [blame]
njnf76d27a2009-05-28 01:53:07 +00001
2/*--------------------------------------------------------------------*/
3/*--- Launching valgrind launcher-darwin.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
9
Elliott Hughesed398002017-06-21 14:41:24 -070010 Copyright (C) 2000-2017 Julian Seward
njnf76d27a2009-05-28 01:53:07 +000011 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"
njna192daf2009-07-01 02:39:26 +000053#include "pub_core_vki.h" // Avoids warnings from pub_core_libcfile.h
njnf76d27a2009-05-28 01:53:07 +000054#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[] = {
sewardj9893a752014-08-13 09:04:02 +000062 { 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" }
njnf76d27a2009-05-28 01:53:07 +000071};
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 */
njna192daf2009-07-01 02:39:26 +0000145static const char *select_arch(
146 const char *clientname, cpu_type_t default_cputype,
147 const char *default_arch)
njnf76d27a2009-05-28 01:53:07 +0000148{
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
njncc7b2182009-06-26 04:35:51 +0000156 bytes = read(fd, buf, sizeof(buf));
157 close(fd);
njnf76d27a2009-05-28 01:53:07 +0000158 if (bytes != sizeof(buf)) {
njnf76d27a2009-05-28 01:53:07 +0000159 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 );
njncde90d32009-07-22 22:41:38 +0000284 VG_(debugLog)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib);
njnf76d27a2009-05-28 01:53:07 +0000285 }
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);
rhyskidd8df59972015-03-07 14:57:39 +0000294 memset(&valid_archs[i], 0, sizeof(valid_archs[i]));
njnf76d27a2009-05-28 01:53:07 +0000295 } 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
Elliott Hughesed398002017-06-21 14:41:24 -0700360 invocations of valgrind on child processes. */
njnf76d27a2009-05-28 01:53:07 +0000361 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
Elliott Hughesed398002017-06-21 14:41:24 -0700413 /* Build the stage2 invocation, and execve it. Bye! */
njnf76d27a2009-05-28 01:53:07 +0000414 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}