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-2013 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 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#include <limits.h> // PATH_MAX 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/* Report fatal errors */ 65__attribute__((noreturn)) 66static 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 */ 82static const char *find_client(const char *clientname) 83{ 84 char *fullname; 85 const char *path = getenv("PATH"); 86 const char *colon; 87 88 assert(clientname != NULL); 89 90 if (path == NULL) return clientname; 91 92 /* Make the size of the FULLNAME buffer large enough. */ 93 unsigned need = strlen(path) + strlen("/") + strlen(clientname) + 1; 94 95 fullname = malloc(need); 96 if (fullname == NULL) 97 barf("malloc of fullname failed."); 98 99 while (path) 100 { 101 if ((colon = strchr(path, ':')) == NULL) 102 { 103 strcpy(fullname, path); 104 path = NULL; 105 } 106 else 107 { 108 strncpy(fullname, path, colon - path); 109 fullname[colon - path] = '\0'; 110 path = colon + 1; 111 } 112 113 strcat(fullname, "/"); 114 strcat(fullname, clientname); 115 116 if (access(fullname, R_OK|X_OK) == 0) 117 return fullname; 118 } 119 free(fullname); 120 121 return clientname; 122} 123 124/* Examine the client and work out which platform it is for */ 125static const char *select_platform(const char *clientname) 126{ 127 int fd; 128 char header[4096]; 129 ssize_t n_bytes; 130 const char *platform = NULL; 131 132 VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); 133 134 if (strchr(clientname, '/') == NULL) 135 clientname = find_client(clientname); 136 137 VG_(debugLog)(2, "launcher", "selecting platform for '%s'\n", clientname); 138 139 if ((fd = open(clientname, O_RDONLY)) < 0) 140 return NULL; 141 // barf("open(%s): %s", clientname, strerror(errno)); 142 143 VG_(debugLog)(2, "launcher", "opened '%s'\n", clientname); 144 145 n_bytes = read(fd, header, sizeof(header)); 146 close(fd); 147 if (n_bytes < 2) { 148 return NULL; 149 } 150 151 VG_(debugLog)(2, "launcher", "read %ld bytes from '%s'\n", 152 (long int)n_bytes, clientname); 153 154 if (header[0] == '#' && header[1] == '!') { 155 int i = 2; 156 char *interp = (char *)header + 2; 157 158 // Skip whitespace. 159 while (1) { 160 if (i == n_bytes) return NULL; 161 if (' ' != header[i] && '\t' != header[i]) break; 162 i++; 163 } 164 165 // Get the interpreter name. 166 interp = &header[i]; 167 while (1) { 168 if (i == n_bytes) break; 169 if (isspace(header[i])) break; 170 i++; 171 } 172 if (i == n_bytes) return NULL; 173 header[i] = '\0'; 174 175 platform = select_platform(interp); 176 177 } else if (n_bytes >= SELFMAG && memcmp(header, ELFMAG, SELFMAG) == 0) { 178 179 if (n_bytes >= sizeof(Elf32_Ehdr) && header[EI_CLASS] == ELFCLASS32) { 180 const Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header; 181 182 if (header[EI_DATA] == ELFDATA2LSB) { 183 if (ehdr->e_machine == EM_386 && 184 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 185 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 186 platform = "x86-linux"; 187 } 188 else 189 if (ehdr->e_machine == EM_ARM && 190 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 191 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 192 platform = "arm-linux"; 193 } 194 else 195 if (ehdr->e_machine == EM_MIPS && 196 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 197 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 198 platform = "mips32-linux"; 199 } 200 } 201 else if (header[EI_DATA] == ELFDATA2MSB) { 202 if (ehdr->e_machine == EM_PPC && 203 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 204 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 205 platform = "ppc32-linux"; 206 } 207 else 208 if (ehdr->e_machine == EM_MIPS && 209 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 210 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 211 platform = "mips32-linux"; 212 } 213 } 214 215 } else if (n_bytes >= sizeof(Elf64_Ehdr) && header[EI_CLASS] == ELFCLASS64) { 216 const Elf64_Ehdr *ehdr = (Elf64_Ehdr *)header; 217 218 if (header[EI_DATA] == ELFDATA2LSB) { 219 if (ehdr->e_machine == EM_X86_64 && 220 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 221 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 222 platform = "amd64-linux"; 223 } else if (ehdr->e_machine == EM_MIPS && 224 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 225 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 226 platform = "mips64-linux"; 227 } else if (ehdr->e_machine == EM_AARCH64 && 228 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 229 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 230 platform = "arm64-linux"; 231 } 232 } else if (header[EI_DATA] == ELFDATA2MSB) { 233# if !defined(VGPV_arm_linux_android) \ 234 && !defined(VGPV_x86_linux_android) \ 235 && !defined(VGPV_mips32_linux_android) 236 if (ehdr->e_machine == EM_PPC64 && 237 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 238 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 239 platform = "ppc64-linux"; 240 } 241 else 242 if (ehdr->e_machine == EM_S390 && 243 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV || 244 ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) { 245 platform = "s390x-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 } 251# endif 252 } 253 } 254 } 255 256 VG_(debugLog)(2, "launcher", "selected platform '%s'\n", 257 platform ? platform : "unknown"); 258 259 return platform; 260} 261 262/* Where we expect to find all our aux files */ 263static const char *valgrind_lib = VG_LIBDIR; 264 265int main(int argc, char** argv, char** envp) 266{ 267 int i, j, loglevel, r; 268 const char *toolname = NULL; 269 const char *clientname = NULL; 270 const char *platform; 271 const char *default_platform; 272 const char *cp; 273 char *toolfile; 274 char launcher_name[PATH_MAX+1]; 275 char* new_line; 276 char** new_env; 277 278 /* Start the debugging-log system ASAP. First find out how many 279 "-d"s were specified. This is a pre-scan of the command line. 280 At the same time, look for the tool name. */ 281 loglevel = 0; 282 for (i = 1; i < argc; i++) { 283 if (argv[i][0] != '-') { 284 clientname = argv[i]; 285 break; 286 } 287 if (0 == strcmp(argv[i], "--")) { 288 if (i+1 < argc) 289 clientname = argv[i+1]; 290 break; 291 } 292 if (0 == strcmp(argv[i], "-d")) 293 loglevel++; 294 if (0 == strncmp(argv[i], "--tool=", 7)) 295 toolname = argv[i] + 7; 296 } 297 298 /* ... and start the debug logger. Now we can safely emit logging 299 messages all through startup. */ 300 VG_(debugLog_startup)(loglevel, "Stage 1"); 301 302 /* Make sure we know which tool we're using */ 303 if (toolname) { 304 VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname); 305 } else { 306 VG_(debugLog)(1, "launcher", 307 "no tool requested, defaulting to 'memcheck'\n"); 308 toolname = "memcheck"; 309 } 310 311 /* Select a platform to use if we can't decide that by looking at 312 the executable (eg because it's a shell script). Note that the 313 default_platform is not necessarily either the primary or 314 secondary build target. Instead it's chosen to maximise the 315 chances that /bin/sh will work on it. Hence for a primary 316 target of ppc64-linux we still choose ppc32-linux as the default 317 target, because on most ppc64-linux setups, the basic /bin, 318 /usr/bin, etc, stuff is built in 32-bit mode, not 64-bit 319 mode. */ 320 if ((0==strcmp(VG_PLATFORM,"x86-linux")) || 321 (0==strcmp(VG_PLATFORM,"amd64-linux")) || 322 (0==strcmp(VG_PLATFORM,"ppc32-linux")) || 323 (0==strcmp(VG_PLATFORM,"ppc64-linux")) || 324 (0==strcmp(VG_PLATFORM,"arm-linux")) || 325 (0==strcmp(VG_PLATFORM,"arm64-linux")) || 326 (0==strcmp(VG_PLATFORM,"s390x-linux")) || 327 (0==strcmp(VG_PLATFORM,"mips32-linux")) || 328 (0==strcmp(VG_PLATFORM,"mips64-linux"))) 329 default_platform = VG_PLATFORM; 330 else 331 barf("Unknown VG_PLATFORM '%s'", VG_PLATFORM); 332 333 /* Work out what platform to use, or use the default platform if 334 not possible. */ 335 if (clientname == NULL) { 336 VG_(debugLog)(1, "launcher", 337 "no client specified, defaulting platform to '%s'\n", 338 default_platform); 339 platform = default_platform; 340 } else if ((platform = select_platform(clientname)) != NULL) { 341 VG_(debugLog)(1, "launcher", "selected platform '%s'\n", platform); 342 } else { 343 VG_(debugLog)(1, "launcher", 344 "no platform detected, defaulting platform to '%s'\n", 345 default_platform); 346 platform = default_platform; 347 } 348 349 /* Figure out the name of this executable (viz, the launcher), so 350 we can tell stage2. stage2 will use the name for recursive 351 invocations of valgrind on child processes. */ 352 memset(launcher_name, 0, PATH_MAX+1); 353 r = readlink("/proc/self/exe", launcher_name, PATH_MAX); 354 if (r == -1) { 355 /* If /proc/self/exe can't be followed, don't give up. Instead 356 continue with an empty string for VALGRIND_LAUNCHER. In the 357 sys_execve wrapper, this is tested, and if found to be empty, 358 fail the execve. */ 359 fprintf(stderr, "valgrind: warning (non-fatal): " 360 "readlink(\"/proc/self/exe\") failed.\n"); 361 fprintf(stderr, "valgrind: continuing, however --trace-children=yes " 362 "will not work.\n"); 363 } 364 365 /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */ 366 new_line = malloc(strlen(VALGRIND_LAUNCHER) + 1 367 + strlen(launcher_name) + 1); 368 if (new_line == NULL) 369 barf("malloc of new_line failed."); 370 strcpy(new_line, VALGRIND_LAUNCHER); 371 strcat(new_line, "="); 372 strcat(new_line, launcher_name); 373 374 for (j = 0; envp[j]; j++) 375 ; 376 new_env = malloc((j+2) * sizeof(char*)); 377 if (new_env == NULL) 378 barf("malloc of new_env failed."); 379 for (i = 0; i < j; i++) 380 new_env[i] = envp[i]; 381 new_env[i++] = new_line; 382 new_env[i++] = NULL; 383 assert(i == j+2); 384 385 /* Establish the correct VALGRIND_LIB. */ 386 cp = getenv(VALGRIND_LIB); 387 388 if (cp != NULL) 389 valgrind_lib = cp; 390 391 /* Build the stage2 invocation, and execve it. Bye! */ 392 toolfile = malloc(strlen(valgrind_lib) + strlen(toolname) + strlen(platform) + 3); 393 if (toolfile == NULL) 394 barf("malloc of toolfile failed."); 395 sprintf(toolfile, "%s/%s-%s", valgrind_lib, toolname, platform); 396 397 VG_(debugLog)(1, "launcher", "launching %s\n", toolfile); 398 399 execve(toolfile, argv, new_env); 400 401 fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s': %s\n", 402 toolname, platform, strerror(errno)); 403 404 exit(1); 405} 406