2 /*--------------------------------------------------------------------*/
3 /*--- Launching valgrind launcher-darwin.c ---*/
4 /*--------------------------------------------------------------------*/
7 This file is part of Valgrind, a dynamic binary instrumentation
10 Copyright (C) 2000-2017 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, see <http://www.gnu.org/licenses/>.
26 The GNU General Public License is contained in the file COPYING.
29 /* Note: this is a "normal" program and not part of Valgrind proper,
30 and so it doesn't have to conform to Valgrind's arcane rules on
31 no-glibc-usage etc. */
43 #include <sys/param.h>
47 #include <mach-o/fat.h>
48 #include <mach-o/loader.h>
50 #include "pub_core_debuglog.h"
51 #include "pub_core_vki.h" // Avoids warnings from pub_core_libcfile.h
52 #include "pub_core_libcproc.h" // For VALGRIND_LIB, VALGRIND_LAUNCHER
53 #include "pub_core_ume.h"
57 const char *apple_name
; // e.g. x86_64
58 const char *valgrind_name
; // e.g. amd64
60 { CPU_TYPE_X86
, "i386", "x86" },
61 { CPU_TYPE_X86_64
, "x86_64", "amd64" },
62 { CPU_TYPE_ARM
, "arm", "arm" },
63 /* Not that it's actually relevant, since we don't support PPC on
64 MacOS X, but .. the Apple PPC descriptors refer to the BE
65 variant, since the LE variant is something that appeared long
66 after Apple dropped PPC. */
67 { CPU_TYPE_POWERPC
, "ppc", "ppc32" },
68 { CPU_TYPE_POWERPC64
, "ppc64", "ppc64be" }
70 static int valid_archs_count
= sizeof(valid_archs
)/sizeof(valid_archs
[0]);
72 static const char *name_for_cputype(cpu_type_t cputype
)
75 for (i
= 0; i
< valid_archs_count
; i
++) {
76 if (valid_archs
[i
].cputype
== cputype
) {
77 return valid_archs
[i
].valgrind_name
;
83 /* Report fatal errors */
84 __attribute__((noreturn
))
85 static void barf ( const char *format
, ... )
89 va_start(vargs
, format
);
90 fprintf(stderr
, "valgrind: ");
91 vfprintf(stderr
, format
, vargs
);
92 fprintf(stderr
, "\n");
100 /* Search the path for the client program */
101 static const char *find_client(const char *clientname
)
103 static char fullname
[PATH_MAX
];
104 const char *path
= getenv("PATH");
109 if ((colon
= strchr(path
, ':')) == NULL
)
111 strcpy(fullname
, path
);
116 memcpy(fullname
, path
, colon
- path
);
117 fullname
[colon
- path
] = '\0';
121 strcat(fullname
, "/");
122 strcat(fullname
, clientname
);
124 if (access(fullname
, R_OK
|X_OK
) == 0)
131 static int fat_has_cputype(struct fat_header
*fh
, cpu_type_t cputype
)
133 struct fat_arch
*fa
= (struct fat_arch
*)(fh
+1);
134 uint32_t nfat_arch
= ntohl(fh
->nfat_arch
);
136 for (i
= 0; i
< nfat_arch
; i
++) {
137 if (ntohl(fa
[i
].cputype
) == cputype
) return 1;
142 /* Examine the client and work out which arch it is for */
143 static const char *select_arch(
144 const char *clientname
, cpu_type_t default_cputype
,
145 const char *default_arch
)
149 int fd
= open(find_client(clientname
), O_RDONLY
);
151 barf("%s: %s", clientname
, strerror(errno
));
154 bytes
= read(fd
, buf
, sizeof(buf
));
156 if (bytes
!= sizeof(buf
)) {
160 // If it's thin, return that arch.
162 struct mach_header
*mh
= (struct mach_header
*)buf
;
163 if (mh
->magic
== MH_MAGIC
|| mh
->magic
== MH_MAGIC_64
) {
164 return name_for_cputype(mh
->cputype
);
165 } else if (mh
->magic
== MH_CIGAM
|| mh
->magic
== MH_CIGAM_64
) {
166 return name_for_cputype(OSSwapInt32(mh
->cputype
));
170 // If it's fat, look for a good arch.
172 struct fat_header
*fh
= (struct fat_header
*)buf
;
173 if (ntohl(fh
->magic
) == FAT_MAGIC
) {
174 uint32_t nfat_arch
= ntohl(fh
->nfat_arch
);
176 // If only one fat arch, use it.
177 if (nfat_arch
== 1) {
178 struct fat_arch
*fa
= (struct fat_arch
*)(fh
+1);
179 return name_for_cputype(ntohl(fa
->cputype
));
181 // Scan fat headers for default arch.
182 if (fat_has_cputype(fh
, default_cputype
)) {
186 // Scan fat headers for any supported arch.
187 for (i
= 0; i
< valid_archs_count
; i
++) {
188 if (fat_has_cputype(fh
, valid_archs
[i
].cputype
)) {
189 return valid_archs
[i
].valgrind_name
;
199 /* Where we expect to find all our aux files */
200 static const char *valgrind_lib
;
202 int main(int argc
, char** argv
, char** envp
)
205 const char *toolname
= NULL
;
206 const char *clientname
= NULL
;
207 int clientname_arg
= 0;
208 const char *archname
= NULL
;
210 const char *default_arch
;
211 cpu_type_t default_cputype
;
213 char launcher_name
[PATH_MAX
+1];
221 /* Start the debugging-log system ASAP. First find out how many
222 "-d"s were specified. This is a pre-scan of the command line.
223 At the same time, look for the tool name. */
225 for (i
= 1; i
< argc
; i
++) {
226 if (argv
[i
][0] != '-') {
227 clientname
= argv
[i
];
231 if (0 == strcmp(argv
[i
], "--")) {
233 clientname
= argv
[i
+1];
238 if (0 == strcmp(argv
[i
], "-d"))
240 if (0 == strncmp(argv
[i
], "--tool=", 7))
241 toolname
= argv
[i
] + 7;
242 if (0 == strncmp(argv
[i
], "--arch=", 7))
243 archname
= argv
[i
] + 7;
246 /* ... and start the debug logger. Now we can safely emit logging
247 messages all through startup. */
248 VG_(debugLog_startup
)(loglevel
, "Stage 1");
250 /* Make sure we know which tool we're using */
252 VG_(debugLog
)(1, "launcher", "tool '%s' requested\n", toolname
);
254 VG_(debugLog
)(1, "launcher",
255 "no tool requested, defaulting to 'memcheck'\n");
256 toolname
= "memcheck";
259 /* Find the real executable if clientname is an app bundle. */
262 if (0 == stat(clientname
, &st
) && (st
.st_mode
& S_IFDIR
)) {
263 char *copy
= strdup(clientname
);
264 char *appname
= basename(copy
);
265 char *dot
= strrchr(appname
, '.');
269 asprintf(&newclient
, "%s/Contents/MacOS/%s", clientname
, appname
);
270 VG_(debugLog
)(1, "launcher", "Using executable in app bundle: %s\n", newclient
);
271 clientname
= newclient
;
272 argv
[clientname_arg
] = newclient
;
278 /* Establish the correct VALGRIND_LIB. */
280 cp
= getenv(VALGRIND_LIB
);
281 valgrind_lib
= ( cp
== NULL
? VG_LIBDIR
: cp
);
282 VG_(debugLog
)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib
);
285 /* Find installed architectures. Use vgpreload_core-<platform>.so as the
286 * indicator of whether the platform is installed. */
287 for (i
= 0; i
< valid_archs_count
; i
++) {
288 char *vgpreload_core
;
289 asprintf(&vgpreload_core
, "%s/vgpreload_core-%s-darwin.so", valgrind_lib
, valid_archs
[i
].valgrind_name
);
290 if (access(vgpreload_core
, R_OK
|X_OK
) != 0) {
291 VG_(debugLog
)(1, "launcher", "arch '%s' IS NOT installed\n", valid_archs
[i
].valgrind_name
);
292 memset(&valid_archs
[i
], 0, sizeof(valid_archs
[i
]));
294 VG_(debugLog
)(1, "launcher", "arch '%s' IS installed\n", valid_archs
[i
].valgrind_name
);
296 free(vgpreload_core
);
299 /* Find the "default" arch (VGCONF_ARCH_PRI from configure).
300 This is the preferred arch from fat files and the fallback. */
303 for (i
= 0; i
< valid_archs_count
; i
++) {
304 if (!valid_archs
[i
].cputype
) continue;
305 if (0 == strncmp(VG_PLATFORM
, valid_archs
[i
].valgrind_name
,
306 strlen(valid_archs
[i
].valgrind_name
)))
308 default_arch
= valid_archs
[i
].valgrind_name
;
309 default_cputype
= valid_archs
[i
].cputype
;
313 if (i
== valid_archs_count
) barf("Unknown/uninstalled VG_PLATFORM '%s'", VG_PLATFORM
);
314 assert(NULL
!= default_arch
);
315 assert(0 != default_cputype
);
317 /* Work out what arch to use, or use the default arch if not possible. */
318 if (archname
!= NULL
) {
319 // --arch from command line
321 for (i
= 0; i
< valid_archs_count
; i
++) {
322 if (0 == strcmp(archname
, valid_archs
[i
].apple_name
) ||
323 0 == strcmp(archname
, valid_archs
[i
].valgrind_name
))
325 arch
= valid_archs
[i
].valgrind_name
;
329 if (i
== valid_archs_count
) barf("Unknown --arch '%s'", archname
);
330 assert(NULL
!= arch
);
331 VG_(debugLog
)(1, "launcher", "using arch '%s' from --arch=%s\n",
334 else if (clientname
== NULL
) {
335 // no client executable; use default as fallback
336 VG_(debugLog
)(1, "launcher",
337 "no client specified, defaulting arch to '%s'\n",
341 else if ((arch
= select_arch(clientname
, default_cputype
,default_arch
))) {
342 // arch from client executable
343 VG_(debugLog
)(1, "launcher", "selected arch '%s'\n", arch
);
346 // nothing found in client executable; use default as fallback
347 VG_(debugLog
)(1, "launcher",
348 "no arch detected, defaulting arch to '%s'\n",
353 cwd
= getcwd(NULL
, 0);
354 if (!cwd
) barf("Current directory no longer exists.");
356 /* Figure out the name of this executable (viz, the launcher), so
357 we can tell stage2. stage2 will use the name for recursive
358 invocations of valgrind on child processes. */
359 memset(launcher_name
, 0, PATH_MAX
+1);
360 for (i
= 0; envp
[i
]; i
++)
361 ; /* executable path is after last envp item */
362 /* envp[i] == NULL ; envp[i+1] == executable_path */
363 if (envp
[i
+1][0] != '/') {
364 strcpy(launcher_name
, cwd
);
365 strcat(launcher_name
, "/");
367 if (strlen(launcher_name
) + strlen(envp
[i
+1]) > PATH_MAX
)
368 barf("launcher path is too long");
369 strcat(launcher_name
, envp
[i
+1]);
370 VG_(debugLog
)(1, "launcher", "launcher_name = %s\n", launcher_name
);
372 /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */
373 asprintf(&new_line
, VALGRIND_LAUNCHER
"=%s", launcher_name
);
375 /* tediously augment the env: VALGRIND_STARTUP_PWD_%PID_XYZZY=current_working_dir */
376 asprintf(&set_cwd
, "VALGRIND_STARTUP_PWD_%u_XYZZY=%s", getppid(), cwd
);
378 // Note that Apple binaries get a secret fourth arg, "char* apple", which
379 // contains the executable path. Don't forget about it.
380 for (j
= 0; envp
[j
]; j
++)
382 new_env
= malloc((j
+4) * sizeof(char*));
384 barf("malloc of new_env failed.");
385 for (i
= 0; i
< j
; i
++)
386 new_env
[i
] = envp
[i
];
387 new_env
[i
++] = new_line
;
388 new_env
[i
++] = set_cwd
;
390 new_env
[i
] = envp
[i
-2]; // the 'apple' arg == the executable_path
393 /* tediously edit env: hide dyld options from valgrind's captive dyld */
394 for (i
= 0; envp
[i
]; i
++) {
395 if (0 == strncmp(envp
[i
], "DYLD_", 5)) {
396 envp
[i
][0] = 'V'; /* VYLD_; changed back by initimg-darwin */
400 /* tediously edit argv: remove --arch= */
401 new_argv
= malloc((1+argc
) * sizeof(char *));
402 for (i
= 0, new_argc
= 0; i
< argc
; i
++) {
403 if (0 == strncmp(argv
[i
], "--arch=", 7)) {
406 new_argv
[new_argc
++] = argv
[i
];
409 new_argv
[new_argc
++] = NULL
;
411 /* Build the stage2 invocation, and execve it. Bye! */
412 asprintf(&toolfile
, "%s/%s-%s-darwin", valgrind_lib
, toolname
, arch
);
413 if (access(toolfile
, R_OK
|X_OK
) != 0) {
414 barf("tool '%s' not installed (%s) (%s)", toolname
, toolfile
, strerror(errno
));
417 VG_(debugLog
)(1, "launcher", "launching %s\n", toolfile
);
419 execve(toolfile
, new_argv
, new_env
);
421 fprintf(stderr
, "valgrind: failed to start tool '%s' for platform '%s-darwin': %s\n",
422 toolname
, arch
, strerror(errno
));