1 2 /*--------------------------------------------------------------------*/ 3 /*--- Launching valgrind m_launcher.c ---*/ 4 /*--------------------------------------------------------------------*/ 5 6 /* 7 This file is part of Valgrind, a dynamic binary instrumentation 8 framework. 9 10 Copyright (C) 2000-2012 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 valgrind headers before system headers to avoid problems 36 with the system headers #defining things which are used as names 37 of structure members in vki headers. */ 38 39 #include "pub_core_debuglog.h" 40 #include "pub_core_vki.h" // Avoids warnings from 41 // pub_core_libcfile.h 42 #include "pub_core_libcproc.h" // For VALGRIND_LIB, VALGRIND_LAUNCHER 43 #include "pub_core_ume.h" 44 45 #include <assert.h> 46 #include <ctype.h> 47 #include <elf.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 55 56 57 #define PATH_MAX 4096 /* POSIX refers to this a lot but I dunno 58 where it is defined */ 59 60 #ifndef EM_X86_64 61 #define EM_X86_64 62 // elf.h doesn't define this on some older systems 62 #endif 63 64 /* Report fatal errors */ 65 __attribute__((noreturn)) 66 static void barf ( const char *format, ... ) 67 { 68 va_list vargs; 69 70 va_start(vargs, format); 71 fprintf(stderr, "valgrind: Cannot continue: "); 72 vfprintf(stderr, format, vargs); 73 fprintf(stderr, "\n"); 74 va_end(vargs); 75 76 exit(1); 77 /*NOTREACHED*/ 78 assert(0); 79 } 80 81 /* Search the path for the client program */ 82 static const char *find_client(const char *clientname) 83 { 84 static char fullname[PATH_MAX]; 85 const char *path = getenv("PATH"); 86 const char *colon; 87 88 while (path) 89 { 90 if ((colon = strchr(path, ':')) == NULL) 91 { 92 strcpy(fullname, path); 93 path = NULL; 94 } 95 else 96 { 97 memcpy(fullname, path, colon - path); 98 fullname[colon - path] = '\0'; 99 path = colon + 1; 100 } 101 102 strcat(fullname, "/"); 103 strcat(fullname, clientname); 104 105 if (access(fullname, R_OK|X_OK) == 0) 106 return fullname; 107 } 108 109 return clientname; 110 } 111 112 /* Examine the client and work out which platform it is for */ 113 static const char *select_platform(const char *clientname) 114 { 115 int fd; 116 uint8_t header[4096]; 117 ssize_t n_bytes; 118 const char *platform = NULL; 119 120 VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); 121 122 if (strchr(clientname, '/') == NULL) 123 clientname = find_client(clientname); 124 125 VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); 126 127 if ((fd = open(clientname, O_RDONLY)) < 0) 128 return NULL; 129 // barf("open(%s): %s", clientname, strerror(errno)); 130 131 VG_(debugLog)(2, "launcher", "opened '%s'\n", clientname); 132 133 n_bytes = read(fd, header, sizeof(header)); 134 close(fd); 135 if (n_bytes < 2) { 136 return NULL; 137 } 138 139 VG_(debugLog)(2, "launcher", "read %ld bytes from '%s'\n", 140 (long int)n_bytes, clientname); 141 142 if (header[0] == '#' && header[1] == '!') { 143 int i = 2; 144 char *interp = (char *)header + 2; 145 146 // Skip whitespace. 147 while (1) { 148 if (i == n_bytes) return NULL; 149 if (' ' != header[i] && '\t' != header[i]) break; 150 i++; 151 } 152 153 // Get the interpreter name. 154 interp = &header[i]; 155 while (1) { 156 if (i == n_bytes) break; 157 if (isspace(header[i])) break; 158 i++; 159 } 160 if (i == n_bytes) return NULL; 161 header[i] = '\0'; 162 163 platform = select_platform(interp); 164 165 } else if (n_bytes >= SELFMAG && memcmp(header, ELFMAG, SELFMAG) == 0) { 166 167 if (n_bytes >= sizeof(Elf32_Ehdr) && header[EI_CLASS] == ELFCLASS32) { 168 const Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header; 169 170 if (header[EI_DATA] == ELFDATA2LSB) { 171 if (ehdr->e_machine == EM_386 && 172 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 173 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 174 platform = "x86-linux"; 175 } 176 else 177 if (ehdr->e_machine == EM_ARM && 178 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 179 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 180 platform = "arm-linux"; 181 } 182 else 183 if (ehdr->e_machine == EM_MIPS && 184 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 185 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 186 platform = "mips32-linux"; 187 } 188 } 189 else if (header[EI_DATA] == ELFDATA2MSB) { 190 if (ehdr->e_machine == EM_PPC && 191 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 192 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 193 platform = "ppc32-linux"; 194 } 195 else 196 if (ehdr->e_machine == EM_MIPS && 197 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 198 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 199 platform = "mips32-linux"; 200 } 201 } 202 203 } else if (n_bytes >= sizeof(Elf64_Ehdr) && header[EI_CLASS] == ELFCLASS64) { 204 const Elf64_Ehdr *ehdr = (Elf64_Ehdr *)header; 205 206 if (header[EI_DATA] == ELFDATA2LSB) { 207 if (ehdr->e_machine == EM_X86_64 && 208 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 209 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 210 platform = "amd64-linux"; 211 } 212 } else if (header[EI_DATA] == ELFDATA2MSB) { 213 # if !defined(VGPV_arm_linux_android) && !defined(VGPV_x86_linux_android) 214 if (ehdr->e_machine == EM_PPC64 && 215 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 216 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 217 platform = "ppc64-linux"; 218 } 219 else 220 if (ehdr->e_machine == EM_S390 && 221 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 222 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 223 platform = "s390x-linux"; 224 } 225 # endif 226 } 227 } 228 } 229 230 VG_(debugLog)(2, "launcher", "selected platform '%s'\n", 231 platform ? platform : "unknown"); 232 233 return platform; 234 } 235 236 /* Where we expect to find all our aux files */ 237 static const char *valgrind_lib = VG_LIBDIR; 238 239 int main(int argc, char** argv, char** envp) 240 { 241 int i, j, loglevel, r; 242 const char *toolname = NULL; 243 const char *clientname = NULL; 244 const char *platform; 245 const char *default_platform; 246 const char *cp; 247 char *toolfile; 248 char launcher_name[PATH_MAX+1]; 249 char* new_line; 250 char** new_env; 251 252 /* Start the debugging-log system ASAP. First find out how many 253 "-d"s were specified. This is a pre-scan of the command line. 254 At the same time, look for the tool name. */ 255 loglevel = 0; 256 for (i = 1; i < argc; i++) { 257 if (argv[i][0] != '-') { 258 clientname = argv[i]; 259 break; 260 } 261 if (0 == strcmp(argv[i], "--")) { 262 if (i+1 < argc) 263 clientname = argv[i+1]; 264 break; 265 } 266 if (0 == strcmp(argv[i], "-d")) 267 loglevel++; 268 if (0 == strncmp(argv[i], "--tool=", 7)) 269 toolname = argv[i] + 7; 270 } 271 272 /* ... and start the debug logger. Now we can safely emit logging 273 messages all through startup. */ 274 VG_(debugLog_startup)(loglevel, "Stage 1"); 275 276 /* Make sure we know which tool we're using */ 277 if (toolname) { 278 VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname); 279 } else { 280 VG_(debugLog)(1, "launcher", 281 "no tool requested, defaulting to 'memcheck'\n"); 282 toolname = "memcheck"; 283 } 284 285 /* Select a platform to use if we can't decide that by looking at 286 the executable (eg because it's a shell script). Note that the 287 default_platform is not necessarily either the primary or 288 secondary build target. Instead it's chosen to maximise the 289 chances that /bin/sh will work on it. Hence for a primary 290 target of ppc64-linux we still choose ppc32-linux as the default 291 target, because on most ppc64-linux setups, the basic /bin, 292 /usr/bin, etc, stuff is built in 32-bit mode, not 64-bit 293 mode. */ 294 if ((0==strcmp(VG_PLATFORM,"x86-linux")) || 295 (0==strcmp(VG_PLATFORM,"amd64-linux")) || 296 (0==strcmp(VG_PLATFORM,"ppc32-linux")) || 297 (0==strcmp(VG_PLATFORM,"ppc64-linux")) || 298 (0==strcmp(VG_PLATFORM,"arm-linux")) || 299 (0==strcmp(VG_PLATFORM,"s390x-linux")) || 300 (0==strcmp(VG_PLATFORM,"mips32-linux"))) 301 default_platform = VG_PLATFORM; 302 else 303 barf("Unknown VG_PLATFORM '%s'", VG_PLATFORM); 304 305 /* Work out what platform to use, or use the default platform if 306 not possible. */ 307 if (clientname == NULL) { 308 VG_(debugLog)(1, "launcher", 309 "no client specified, defaulting platform to '%s'\n", 310 default_platform); 311 platform = default_platform; 312 } else if ((platform = select_platform(clientname)) != NULL) { 313 VG_(debugLog)(1, "launcher", "selected platform '%s'\n", platform); 314 } else { 315 VG_(debugLog)(1, "launcher", 316 "no platform detected, defaulting platform to '%s'\n", 317 default_platform); 318 platform = default_platform; 319 } 320 321 /* Figure out the name of this executable (viz, the launcher), so 322 we can tell stage2. stage2 will use the name for recursive 323 invocations of valgrind on child processes. */ 324 memset(launcher_name, 0, PATH_MAX+1); 325 r = readlink("/proc/self/exe", launcher_name, PATH_MAX); 326 if (r == -1) { 327 /* If /proc/self/exe can't be followed, don't give up. Instead 328 continue with an empty string for VALGRIND_LAUNCHER. In the 329 sys_execve wrapper, this is tested, and if found to be empty, 330 fail the execve. */ 331 fprintf(stderr, "valgrind: warning (non-fatal): " 332 "readlink(\"/proc/self/exe\") failed.\n"); 333 fprintf(stderr, "valgrind: continuing, however --trace-children=yes " 334 "will not work.\n"); 335 } 336 337 /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */ 338 new_line = malloc(strlen(VALGRIND_LAUNCHER) + 1 339 + strlen(launcher_name) + 1); 340 if (new_line == NULL) 341 barf("malloc of new_line failed."); 342 strcpy(new_line, VALGRIND_LAUNCHER); 343 strcat(new_line, "="); 344 strcat(new_line, launcher_name); 345 346 for (j = 0; envp[j]; j++) 347 ; 348 new_env = malloc((j+2) * sizeof(char*)); 349 if (new_env == NULL) 350 barf("malloc of new_env failed."); 351 for (i = 0; i < j; i++) 352 new_env[i] = envp[i]; 353 new_env[i++] = new_line; 354 new_env[i++] = NULL; 355 assert(i == j+2); 356 357 /* Establish the correct VALGRIND_LIB. */ 358 cp = getenv(VALGRIND_LIB); 359 360 if (cp != NULL) 361 valgrind_lib = cp; 362 363 /* Build the stage2 invocation, and execve it. Bye! */ 364 toolfile = malloc(strlen(valgrind_lib) + strlen(toolname) + strlen(platform) + 3); 365 if (toolfile == NULL) 366 barf("malloc of toolfile failed."); 367 sprintf(toolfile, "%s/%s-%s", valgrind_lib, toolname, platform); 368 369 VG_(debugLog)(1, "launcher", "launching %s\n", toolfile); 370 371 execve(toolfile, argv, new_env); 372 373 fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s': %s\n", 374 toolname, platform, strerror(errno)); 375 376 exit(1); 377 } 378