2 /*--------------------------------------------------------------------*/
3 /*--- Launching valgrind launcher-darwin.c ---*/
4 /*--------------------------------------------------------------------*/
7 This file is part of Valgrind, a dynamic binary instrumentation
10 Copyright (C) 2000-2013 Julian Seward
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.
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.
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
28 The GNU General Public License is contained in the file COPYING.
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. */
45 #include <sys/param.h>
49 #include <mach-o/fat.h>
50 #include <mach-o/loader.h>
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"
59 const char *apple_name
; // e.g. x86_64
60 const char *valgrind_name
; // e.g. amd64
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" }
72 static int valid_archs_count
= sizeof(valid_archs
)/sizeof(valid_archs
[0]);
74 static const char *name_for_cputype(cpu_type_t cputype
)
77 for (i
= 0; i
< valid_archs_count
; i
++) {
78 if (valid_archs
[i
].cputype
== cputype
) {
79 return valid_archs
[i
].valgrind_name
;
85 /* Report fatal errors */
86 __attribute__((noreturn
))
87 static void barf ( const char *format
, ... )
91 va_start(vargs
, format
);
92 fprintf(stderr
, "valgrind: ");
93 vfprintf(stderr
, format
, vargs
);
94 fprintf(stderr
, "\n");
102 /* Search the path for the client program */
103 static const char *find_client(const char *clientname
)
105 static char fullname
[PATH_MAX
];
106 const char *path
= getenv("PATH");
111 if ((colon
= strchr(path
, ':')) == NULL
)
113 strcpy(fullname
, path
);
118 memcpy(fullname
, path
, colon
- path
);
119 fullname
[colon
- path
] = '\0';
123 strcat(fullname
, "/");
124 strcat(fullname
, clientname
);
126 if (access(fullname
, R_OK
|X_OK
) == 0)
133 static int fat_has_cputype(struct fat_header
*fh
, cpu_type_t cputype
)
135 struct fat_arch
*fa
= (struct fat_arch
*)(fh
+1);
136 uint32_t nfat_arch
= ntohl(fh
->nfat_arch
);
138 for (i
= 0; i
< nfat_arch
; i
++) {
139 if (ntohl(fa
[i
].cputype
) == cputype
) return 1;
144 /* Examine the client and work out which arch it is for */
145 static const char *select_arch(
146 const char *clientname
, cpu_type_t default_cputype
,
147 const char *default_arch
)
151 int fd
= open(find_client(clientname
), O_RDONLY
);
153 barf("%s: %s", clientname
, strerror(errno
));
156 bytes
= read(fd
, buf
, sizeof(buf
));
158 if (bytes
!= sizeof(buf
)) {
162 // If it's thin, return that arch.
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
));
172 // If it's fat, look for a good arch.
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
);
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
));
183 // Scan fat headers for default arch.
184 if (fat_has_cputype(fh
, default_cputype
)) {
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
;
201 /* Where we expect to find all our aux files */
202 static const char *valgrind_lib
;
204 int main(int argc
, char** argv
, char** envp
)
207 const char *toolname
= NULL
;
208 const char *clientname
= NULL
;
209 int clientname_arg
= 0;
210 const char *archname
= NULL
;
212 const char *default_arch
;
213 cpu_type_t default_cputype
;
215 char launcher_name
[PATH_MAX
+1];
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. */
227 for (i
= 1; i
< argc
; i
++) {
228 if (argv
[i
][0] != '-') {
229 clientname
= argv
[i
];
233 if (0 == strcmp(argv
[i
], "--")) {
235 clientname
= argv
[i
+1];
240 if (0 == strcmp(argv
[i
], "-d"))
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;
248 /* ... and start the debug logger. Now we can safely emit logging
249 messages all through startup. */
250 VG_(debugLog_startup
)(loglevel
, "Stage 1");
252 /* Make sure we know which tool we're using */
254 VG_(debugLog
)(1, "launcher", "tool '%s' requested\n", toolname
);
256 VG_(debugLog
)(1, "launcher",
257 "no tool requested, defaulting to 'memcheck'\n");
258 toolname
= "memcheck";
261 /* Find the real executable if clientname is an app bundle. */
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
, '.');
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
;
280 /* Establish the correct VALGRIND_LIB. */
282 cp
= getenv(VALGRIND_LIB
);
283 valgrind_lib
= ( cp
== NULL
? VG_LIBDIR
: cp
);
284 VG_(debugLog
)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib
);
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
]));
296 VG_(debugLog
)(1, "launcher", "arch '%s' IS installed\n", valid_archs
[i
].valgrind_name
);
298 free(vgpreload_core
);
301 /* Find the "default" arch (VGCONF_ARCH_PRI from configure).
302 This is the preferred arch from fat files and the fallback. */
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
)))
310 default_arch
= valid_archs
[i
].valgrind_name
;
311 default_cputype
= valid_archs
[i
].cputype
;
315 if (i
== valid_archs_count
) barf("Unknown/uninstalled VG_PLATFORM '%s'", VG_PLATFORM
);
316 assert(NULL
!= default_arch
);
317 assert(0 != default_cputype
);
319 /* Work out what arch to use, or use the default arch if not possible. */
320 if (archname
!= NULL
) {
321 // --arch from command line
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
))
327 arch
= valid_archs
[i
].valgrind_name
;
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",
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",
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
);
348 // nothing found in client executable; use default as fallback
349 VG_(debugLog
)(1, "launcher",
350 "no arch detected, defaulting arch to '%s'\n",
355 cwd
= getcwd(NULL
, 0);
356 if (!cwd
) barf("Current directory no longer exists.");
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
, "/");
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
);
374 /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */
375 asprintf(&new_line
, VALGRIND_LAUNCHER
"=%s", launcher_name
);
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
);
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
++)
384 new_env
= malloc((j
+4) * sizeof(char*));
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
;
392 new_env
[i
] = envp
[i
-2]; // the 'apple' arg == the executable_path
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 */
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)) {
408 new_argv
[new_argc
++] = argv
[i
];
411 new_argv
[new_argc
++] = NULL
;
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
));
419 VG_(debugLog
)(1, "launcher", "launching %s\n", toolfile
);
421 execve(toolfile
, new_argv
, new_env
);
423 fprintf(stderr
, "valgrind: failed to start tool '%s' for platform '%s-darwin': %s\n",
424 toolname
, arch
, strerror(errno
));