1 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-2017 Julian Seward 11 jseward (at) 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" 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" 56 57 static 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[] = { 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" } 71 }; 72 static int valid_archs_count = sizeof(valid_archs)/sizeof(valid_archs[0]); 73 74 static 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)) 87 static 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 */ 103 static 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 133 static 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 */ 145 static const char *select_arch( 146 const char *clientname, cpu_type_t default_cputype, 147 const char *default_arch) 148 { 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 156 bytes = read(fd, buf, sizeof(buf)); 157 close(fd); 158 if (bytes != sizeof(buf)) { 159 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 */ 202 static const char *valgrind_lib; 203 204 int 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 ); 284 VG_(debugLog)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib); 285 } 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); 294 memset(&valid_archs[i], 0, sizeof(valid_archs[i])); 295 } 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 360 invocations 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, "/"); 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 413 /* Build the stage2 invocation, 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)); 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 } 428