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-2011 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" 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 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[] = { 62 { 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" }, 67}; 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 */ 141static const char *select_arch( 142 const char *clientname, cpu_type_t default_cputype, 143 const char *default_arch) 144{ 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 152 bytes = read(fd, buf, sizeof(buf)); 153 close(fd); 154 if (bytes != sizeof(buf)) { 155 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 ); 280 VG_(debugLog)(1, "launcher", "valgrind_lib = %s\n", valgrind_lib); 281 } 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} 424