1/* -*- mode: C; c-basic-offset: 3; -*- */ 2 3/*--------------------------------------------------------------------*/ 4/*--- Launching valgrind m_launcher.c ---*/ 5/*--------------------------------------------------------------------*/ 6 7/* 8 This file is part of Valgrind, a dynamic binary instrumentation 9 framework. 10 11 Copyright (C) 2000-2013 Julian Seward 12 jseward@acm.org 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30*/ 31 32/* Note: this is a "normal" program and not part of Valgrind proper, 33 and so it doesn't have to conform to Valgrind's arcane rules on 34 no-glibc-usage etc. */ 35 36/* Include valgrind headers before system headers to avoid problems 37 with the system headers #defining things which are used as names 38 of structure members in vki headers. */ 39 40#include "pub_core_debuglog.h" 41#include "pub_core_vki.h" // Avoids warnings from 42 // pub_core_libcfile.h 43#include "pub_core_libcproc.h" // For VALGRIND_LIB, VALGRIND_LAUNCHER 44#include "pub_core_ume.h" 45 46#include <assert.h> 47#include <ctype.h> 48#include <elf.h> 49#include <errno.h> 50#include <fcntl.h> 51#include <stdio.h> 52#include <stdlib.h> 53#include <string.h> 54#include <unistd.h> 55 56#ifndef EM_X86_64 57#define EM_X86_64 62 // elf.h doesn't define this on some older systems 58#endif 59 60#ifndef EM_AARCH64 61#define EM_AARCH64 183 // ditto 62#endif 63 64#ifndef EM_PPC64 65#define EM_PPC64 21 // ditto 66#endif 67 68#ifndef EM_TILEGX 69#define EM_TILEGX 191 70#endif 71 72/* Report fatal errors */ 73__attribute__((noreturn)) 74static void barf ( const char *format, ... ) 75{ 76 va_list vargs; 77 78 va_start(vargs, format); 79 fprintf(stderr, "valgrind: Cannot continue: "); 80 vfprintf(stderr, format, vargs); 81 fprintf(stderr, "\n"); 82 va_end(vargs); 83 84 exit(1); 85 /*NOTREACHED*/ 86 assert(0); 87} 88 89/* Search the path for the client program */ 90static const char *find_client(const char *clientname) 91{ 92 char *fullname; 93 const char *path = getenv("PATH"); 94 const char *colon; 95 96 assert(clientname != NULL); 97 98 if (path == NULL) return clientname; 99 100 /* Make the size of the FULLNAME buffer large enough. */ 101 unsigned need = strlen(path) + strlen("/") + strlen(clientname) + 1; 102 103 fullname = malloc(need); 104 if (fullname == NULL) 105 barf("malloc of fullname failed."); 106 107 while (path) 108 { 109 if ((colon = strchr(path, ':')) == NULL) 110 { 111 strcpy(fullname, path); 112 path = NULL; 113 } 114 else 115 { 116 strncpy(fullname, path, colon - path); 117 fullname[colon - path] = '\0'; 118 path = colon + 1; 119 } 120 121 strcat(fullname, "/"); 122 strcat(fullname, clientname); 123 124 if (access(fullname, R_OK|X_OK) == 0) 125 return fullname; 126 } 127 free(fullname); 128 129 return clientname; 130} 131 132/* Examine the client and work out which platform it is for */ 133static const char *select_platform(const char *clientname) 134{ 135 int fd; 136 char header[4096]; 137 ssize_t n_bytes; 138 const char *platform = NULL; 139 140 VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); 141 142 if (strchr(clientname, '/') == NULL) 143 clientname = find_client(clientname); 144 145 VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); 146 147 if ((fd = open(clientname, O_RDONLY)) < 0) 148 return NULL; 149 // barf("open(%s): %s", clientname, strerror(errno)); 150 151 VG_(debugLog)(2, "launcher", "opened '%s'\n", clientname); 152 153 n_bytes = read(fd, header, sizeof(header)); 154 close(fd); 155 if (n_bytes < 2) { 156 return NULL; 157 } 158 159 VG_(debugLog)(2, "launcher", "read %ld bytes from '%s'\n", 160 (long int)n_bytes, clientname); 161 162 if (header[0] == '#' && header[1] == '!') { 163 int i = 2; 164 165 STATIC_ASSERT(VKI_BINPRM_BUF_SIZE < sizeof header); 166 if (n_bytes > VKI_BINPRM_BUF_SIZE) 167 n_bytes = VKI_BINPRM_BUF_SIZE - 1; 168 header[n_bytes] = '\0'; 169 char *eol = strchr(header, '\n'); 170 if (eol != NULL) 171 *eol = '\0'; 172 173 // Skip whitespace. 174 while (header[i] == ' '|| header[i] == '\t') 175 i++; 176 177 // Get the interpreter name. 178 const char *interp = header + i; 179 180 if (header[i] == '\0') { 181 // No interpreter was found; fall back to default shell 182# if defined(VGPV_arm_linux_android) \ 183 || defined(VGPV_x86_linux_android) \ 184 || defined(VGPV_mips32_linux_android) \ 185 || defined(VGPV_arm64_linux_android) 186 interp = "/system/bin/sh"; 187# else 188 interp = "/bin/sh"; 189# endif 190 } else { 191 while (header[i]) { 192 if (header[i] == ' ' || header[i] == '\t') break; 193 i++; 194 } 195 header[i] = '\0'; 196 } 197 198 platform = select_platform(interp); 199 200 } else if (n_bytes >= SELFMAG && memcmp(header, ELFMAG, SELFMAG) == 0) { 201 202 if (n_bytes >= sizeof(Elf32_Ehdr) && header[EI_CLASS] == ELFCLASS32) { 203 const Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header; 204 205 if (header[EI_DATA] == ELFDATA2LSB) { 206 if (ehdr->e_machine == EM_386 && 207 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 208 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 209 platform = "x86-linux"; 210 } 211 else 212 if (ehdr->e_machine == EM_ARM && 213 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 214 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 215 platform = "arm-linux"; 216 } 217 else 218 if (ehdr->e_machine == EM_MIPS && 219 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 220 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 221 platform = "mips32-linux"; 222 } 223 } 224 else if (header[EI_DATA] == ELFDATA2MSB) { 225 if (ehdr->e_machine == EM_PPC && 226 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 227 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 228 platform = "ppc32-linux"; 229 } 230 else 231 if (ehdr->e_machine == EM_MIPS && 232 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 233 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 234 platform = "mips32-linux"; 235 } 236 } 237 238 } else if (n_bytes >= sizeof(Elf64_Ehdr) && header[EI_CLASS] == ELFCLASS64) { 239 const Elf64_Ehdr *ehdr = (Elf64_Ehdr *)header; 240 241 if (header[EI_DATA] == ELFDATA2LSB) { 242 if (ehdr->e_machine == EM_X86_64 && 243 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 244 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 245 platform = "amd64-linux"; 246 } else if (ehdr->e_machine == EM_MIPS && 247 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 248 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 249 platform = "mips64-linux"; 250 } else if (ehdr->e_machine == EM_TILEGX && 251 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 252 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 253 platform = "tilegx-linux"; 254 } else if (ehdr->e_machine == EM_AARCH64 && 255 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 256 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 257 platform = "arm64-linux"; 258 } else if (ehdr->e_machine == EM_PPC64 && 259 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 260 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 261 platform = "ppc64le-linux"; 262 } 263 } else if (header[EI_DATA] == ELFDATA2MSB) { 264# if !defined(VGPV_arm_linux_android) \ 265 && !defined(VGPV_x86_linux_android) \ 266 && !defined(VGPV_mips32_linux_android) \ 267 && !defined(VGPV_arm64_linux_android) 268 if (ehdr->e_machine == EM_PPC64 && 269 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 270 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 271 platform = "ppc64be-linux"; 272 } 273 else 274 if (ehdr->e_machine == EM_S390 && 275 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 276 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 277 platform = "s390x-linux"; 278 } else if (ehdr->e_machine == EM_MIPS && 279 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 280 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 281 platform = "mips64-linux"; 282 } 283# endif 284 } 285 } 286 } 287 288 VG_(debugLog)(2, "launcher", "selected platform '%s'\n", 289 platform ? platform : "unknown"); 290 291 return platform; 292} 293 294/* Where we expect to find all our aux files */ 295static const char *valgrind_lib = VG_LIBDIR; 296 297int main(int argc, char** argv, char** envp) 298{ 299 int i, j, loglevel, r; 300 const char *toolname = NULL; 301 const char *clientname = NULL; 302 const char *platform; 303 const char *default_platform; 304 const char *cp; 305 char *toolfile; 306 const char *launcher_name; 307 char* new_line; 308 char** new_env; 309 310 /* Start the debugging-log system ASAP. First find out how many 311 "-d"s were specified. This is a pre-scan of the command line. 312 At the same time, look for the tool name. */ 313 loglevel = 0; 314 for (i = 1; i < argc; i++) { 315 if (argv[i][0] != '-') { 316 clientname = argv[i]; 317 break; 318 } 319 if (0 == strcmp(argv[i], "--")) { 320 if (i+1 < argc) 321 clientname = argv[i+1]; 322 break; 323 } 324 if (0 == strcmp(argv[i], "-d")) 325 loglevel++; 326 if (0 == strncmp(argv[i], "--tool=", 7)) 327 toolname = argv[i] + 7; 328 } 329 330 /* ... and start the debug logger. Now we can safely emit logging 331 messages all through startup. */ 332 VG_(debugLog_startup)(loglevel, "Stage 1"); 333 334 /* Make sure we know which tool we're using */ 335 if (toolname) { 336 VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname); 337 } else { 338 VG_(debugLog)(1, "launcher", 339 "no tool requested, defaulting to 'memcheck'\n"); 340 toolname = "memcheck"; 341 } 342 343 /* Select a platform to use if we can't decide that by looking at 344 the executable (eg because it's a shell script). VG_PLATFORM is the 345 default_platform. Its value is defined in coregrind/Makefile.am and 346 typically it is the primary build target. Unless the primary build 347 target is not built is not built in which case VG_PLATFORM is the 348 secondary build target. */ 349 if ((0==strcmp(VG_PLATFORM,"x86-linux")) || 350 (0==strcmp(VG_PLATFORM,"amd64-linux")) || 351 (0==strcmp(VG_PLATFORM,"ppc32-linux")) || 352 (0==strcmp(VG_PLATFORM,"ppc64be-linux")) || 353 (0==strcmp(VG_PLATFORM,"ppc64le-linux")) || 354 (0==strcmp(VG_PLATFORM,"arm-linux")) || 355 (0==strcmp(VG_PLATFORM,"arm64-linux")) || 356 (0==strcmp(VG_PLATFORM,"s390x-linux")) || 357 (0==strcmp(VG_PLATFORM,"tilegx-linux")) || 358 (0==strcmp(VG_PLATFORM,"mips32-linux")) || 359 (0==strcmp(VG_PLATFORM,"mips64-linux"))) 360 default_platform = VG_PLATFORM; 361 else 362 barf("Unknown VG_PLATFORM '%s'", VG_PLATFORM); 363 364 /* Work out what platform to use, or use the default platform if 365 not possible. */ 366 if (clientname == NULL) { 367 VG_(debugLog)(1, "launcher", 368 "no client specified, defaulting platform to '%s'\n", 369 default_platform); 370 platform = default_platform; 371 } else if ((platform = select_platform(clientname)) != NULL) { 372 VG_(debugLog)(1, "launcher", "selected platform '%s'\n", platform); 373 } else { 374 VG_(debugLog)(1, "launcher", 375 "no platform detected, defaulting platform to '%s'\n", 376 default_platform); 377 platform = default_platform; 378 } 379 380 /* Figure out the name of this executable (viz, the launcher), so 381 we can tell stage2. stage2 will use the name for recursive 382 invocations of valgrind on child processes. */ 383 unsigned bufsiz = 0; 384 char *buf = NULL; 385 386 while (42) { 387 bufsiz += 500; 388 buf = realloc(buf, bufsiz); 389 if (buf == NULL) 390 barf("realloc of buf failed."); 391 r = readlink("/proc/self/exe", buf, bufsiz); 392 if (r == -1) { 393 /* If /proc/self/exe can't be followed, don't give up. Instead 394 continue with an empty string for VALGRIND_LAUNCHER. In the 395 sys_execve wrapper, this is tested, and if found to be empty, 396 fail the execve. */ 397 fprintf(stderr, "valgrind: warning (non-fatal): " 398 "readlink(\"/proc/self/exe\") failed.\n"); 399 fprintf(stderr, "valgrind: continuing, however --trace-children=yes " 400 "will not work.\n"); 401 launcher_name = ""; 402 break; 403 } 404 if (r == bufsiz) continue; // buffer to small; retry 405 406 assert(r < bufsiz); // paranoia 407 408 buf[r] = '\0'; 409 launcher_name = buf; 410 break; 411 } 412 413 /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */ 414 new_line = malloc(strlen(VALGRIND_LAUNCHER) + 1 415 + strlen(launcher_name) + 1); 416 if (new_line == NULL) 417 barf("malloc of new_line failed."); 418 strcpy(new_line, VALGRIND_LAUNCHER); 419 strcat(new_line, "="); 420 strcat(new_line, launcher_name); 421 422 for (j = 0; envp[j]; j++) 423 ; 424 new_env = malloc((j+2) * sizeof(char*)); 425 if (new_env == NULL) 426 barf("malloc of new_env failed."); 427 for (i = 0; i < j; i++) 428 new_env[i] = envp[i]; 429 new_env[i++] = new_line; 430 new_env[i++] = NULL; 431 assert(i == j+2); 432 433 /* Establish the correct VALGRIND_LIB. */ 434 cp = getenv(VALGRIND_LIB); 435 436 if (cp != NULL) 437 valgrind_lib = cp; 438 439 /* Build the stage2 invocation, and execve it. Bye! */ 440 toolfile = malloc(strlen(valgrind_lib) + strlen(toolname) + strlen(platform) + 3); 441 if (toolfile == NULL) 442 barf("malloc of toolfile failed."); 443 sprintf(toolfile, "%s/%s-%s", valgrind_lib, toolname, platform); 444 445 VG_(debugLog)(1, "launcher", "launching %s\n", toolfile); 446 447 execve(toolfile, argv, new_env); 448 449 fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s': %s\n", 450 toolname, platform, strerror(errno)); 451 452 exit(1); 453} 454