regtest: add a fdleak filter for write on write on linux arm64
[valgrind.git] / coregrind / launcher-darwin.c
blobeafc21d04f7fa3de6f584b03629f373a6d2e4cfb
2 /*--------------------------------------------------------------------*/
3 /*--- Launching valgrind launcher-darwin.c ---*/
4 /*--------------------------------------------------------------------*/
6 /*
7 This file is part of Valgrind, a dynamic binary instrumentation
8 framework.
10 Copyright (C) 2000-2017 Julian Seward
11 jseward@acm.org
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. */
33 #include <assert.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <libgen.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/mman.h>
43 #include <sys/param.h>
44 #include <sys/stat.h>
45 #include <sys/user.h>
46 #include <unistd.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"
55 static struct {
56 cpu_type_t cputype;
57 const char *apple_name; // e.g. x86_64
58 const char *valgrind_name; // e.g. amd64
59 } valid_archs[] = {
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)
74 int i;
75 for (i = 0; i < valid_archs_count; i++) {
76 if (valid_archs[i].cputype == cputype) {
77 return valid_archs[i].valgrind_name;
80 return NULL;
83 /* Report fatal errors */
84 __attribute__((noreturn))
85 static void barf ( const char *format, ... )
87 va_list vargs;
89 va_start(vargs, format);
90 fprintf(stderr, "valgrind: ");
91 vfprintf(stderr, format, vargs);
92 fprintf(stderr, "\n");
93 va_end(vargs);
95 exit(1);
96 /*NOTREACHED*/
97 assert(0);
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");
105 const char *colon;
107 while (path)
109 if ((colon = strchr(path, ':')) == NULL)
111 strcpy(fullname, path);
112 path = NULL;
114 else
116 memcpy(fullname, path, colon - path);
117 fullname[colon - path] = '\0';
118 path = colon + 1;
121 strcat(fullname, "/");
122 strcat(fullname, clientname);
124 if (access(fullname, R_OK|X_OK) == 0)
125 return fullname;
128 return clientname;
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);
135 uint32_t i;
136 for (i = 0; i < nfat_arch; i++) {
137 if (ntohl(fa[i].cputype) == cputype) return 1;
139 return 0;
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)
147 uint8_t buf[4096];
148 ssize_t bytes;
149 int fd = open(find_client(clientname), O_RDONLY);
150 if (fd < 0) {
151 barf("%s: %s", clientname, strerror(errno));
154 bytes = read(fd, buf, sizeof(buf));
155 close(fd);
156 if (bytes != sizeof(buf)) {
157 return NULL;
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);
175 int i;
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)) {
183 return default_arch;
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;
195 return NULL;
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)
204 int i, j, loglevel;
205 const char *toolname = NULL;
206 const char *clientname = NULL;
207 int clientname_arg = 0;
208 const char *archname = NULL;
209 const char *arch;
210 const char *default_arch;
211 cpu_type_t default_cputype;
212 char *toolfile;
213 char launcher_name[PATH_MAX+1];
214 char* new_line;
215 char* set_cwd;
216 char* cwd;
217 char** new_env;
218 char **new_argv;
219 int new_argc;
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. */
224 loglevel = 0;
225 for (i = 1; i < argc; i++) {
226 if (argv[i][0] != '-') {
227 clientname = argv[i];
228 clientname_arg = i;
229 break;
231 if (0 == strcmp(argv[i], "--")) {
232 if (i+1 < argc) {
233 clientname = argv[i+1];
234 clientname_arg = i;
236 break;
238 if (0 == strcmp(argv[i], "-d"))
239 loglevel++;
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 */
251 if (toolname) {
252 VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname);
253 } else {
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. */
260 if (clientname) {
261 struct stat st;
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, '.');
266 if (dot) {
267 char *newclient;
268 *dot = '\0';
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;
274 free(copy);
278 /* Establish the correct VALGRIND_LIB. */
279 { const char *cp;
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]));
293 } else {
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. */
301 default_arch = NULL;
302 default_cputype = 0;
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;
310 break;
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
320 arch = NULL;
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;
326 break;
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",
332 arch, archname);
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",
338 default_arch);
339 arch = default_arch;
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);
345 else {
346 // nothing found in client executable; use default as fallback
347 VG_(debugLog)(1, "launcher",
348 "no arch detected, defaulting arch to '%s'\n",
349 default_arch);
350 arch = default_arch;
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*));
383 if (new_env == NULL)
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;
389 new_env[i++] = NULL;
390 new_env[i ] = envp[i-2]; // the 'apple' arg == the executable_path
391 assert(i == j+3);
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)) {
404 // skip
405 } else {
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));
424 exit(1);