blob: 83d016a11062908765fac3b9d3318761c658508a [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
10 Copyright (C) 2000-2007 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"
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[] = {
njna192daf2009-07-01 02:39:26 +000062 { 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" },
njnf76d27a2009-05-28 01:53:07 +000067};
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 */
njna192daf2009-07-01 02:39:26 +0000141static const char *select_arch(
142 const char *clientname, cpu_type_t default_cputype,
143 const char *default_arch)
njnf76d27a2009-05-28 01:53:07 +0000144{
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
njncc7b2182009-06-26 04:35:51 +0000152 bytes = read(fd, buf, sizeof(buf));
153 close(fd);
njnf76d27a2009-05-28 01:53:07 +0000154 if (bytes != sizeof(buf)) {
njnf76d27a2009-05-28 01:53:07 +0000155 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 );
njncde90d32009-07-22 22:41:38 +0000280 VG_(debugLog)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib);
njnf76d27a2009-05-28 01:53:07 +0000281 }
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}