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-2015 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))
barf(const char * format,...)74 static 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 */
find_client(const char * clientname)90 static 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 */
select_platform(const char * clientname)133 static 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 defined(VGO_solaris)
207             if (ehdr->e_machine == EM_386 &&
208                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
209                  ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS)) {
210                platform = "x86-solaris";
211             }
212             else
213 #           endif
214             if (ehdr->e_machine == EM_386 &&
215                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
216                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
217                platform = "x86-linux";
218             }
219             else
220             if (ehdr->e_machine == EM_ARM &&
221                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
222                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
223                platform = "arm-linux";
224             }
225             else
226             if (ehdr->e_machine == EM_MIPS &&
227                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
228                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
229                platform = "mips32-linux";
230             }
231          }
232          else if (header[EI_DATA] == ELFDATA2MSB) {
233             if (ehdr->e_machine == EM_PPC &&
234                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
235                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
236                platform = "ppc32-linux";
237             }
238             else
239             if (ehdr->e_machine == EM_MIPS &&
240                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
241                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
242                platform = "mips32-linux";
243             }
244          }
245 
246       } else if (n_bytes >= sizeof(Elf64_Ehdr) && header[EI_CLASS] == ELFCLASS64) {
247          const Elf64_Ehdr *ehdr = (Elf64_Ehdr *)header;
248 
249          if (header[EI_DATA] == ELFDATA2LSB) {
250 #           if defined(VGO_solaris)
251             if (ehdr->e_machine == EM_X86_64 &&
252                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
253                  ehdr->e_ident[EI_OSABI] == ELFOSABI_SOLARIS)) {
254                platform = "amd64-solaris";
255             }
256             else
257 #           endif
258             if (ehdr->e_machine == EM_X86_64 &&
259                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
260                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
261                platform = "amd64-linux";
262             } else if (ehdr->e_machine == EM_MIPS &&
263                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
264                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
265                platform = "mips64-linux";
266             } else if (ehdr->e_machine == EM_TILEGX &&
267                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
268                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
269                platform = "tilegx-linux";
270             } else if (ehdr->e_machine == EM_AARCH64 &&
271                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
272                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
273                platform = "arm64-linux";
274             } else if (ehdr->e_machine == EM_PPC64 &&
275                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
276                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
277                platform = "ppc64le-linux";
278             }
279          } else if (header[EI_DATA] == ELFDATA2MSB) {
280 #           if !defined(VGPV_arm_linux_android) \
281                && !defined(VGPV_x86_linux_android) \
282                && !defined(VGPV_mips32_linux_android) \
283                && !defined(VGPV_arm64_linux_android)
284             if (ehdr->e_machine == EM_PPC64 &&
285                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
286                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
287                platform = "ppc64be-linux";
288             }
289             else
290             if (ehdr->e_machine == EM_S390 &&
291                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
292                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
293                platform = "s390x-linux";
294             } else if (ehdr->e_machine == EM_MIPS &&
295                 (ehdr->e_ident[EI_OSABI] == ELFOSABI_SYSV ||
296                  ehdr->e_ident[EI_OSABI] == ELFOSABI_LINUX)) {
297                platform = "mips64-linux";
298             }
299 #           endif
300          }
301       }
302    }
303 
304    VG_(debugLog)(2, "launcher", "selected platform '%s'\n",
305                  platform ? platform : "unknown");
306 
307    return platform;
308 }
309 
310 /* Where we expect to find all our aux files */
311 static const char *valgrind_lib = VG_LIBDIR;
312 
main(int argc,char ** argv,char ** envp)313 int main(int argc, char** argv, char** envp)
314 {
315    int i, j, loglevel, r;
316    const char *toolname = NULL;
317    const char *clientname = NULL;
318    const char *platform;
319    const char *default_platform;
320    const char *cp;
321    const char *linkname;
322    char *toolfile;
323    const char *launcher_name;
324    char* new_line;
325    char** new_env;
326 
327    /* Start the debugging-log system ASAP.  First find out how many
328       "-d"s were specified.  This is a pre-scan of the command line.
329       At the same time, look for the tool name. */
330    loglevel = 0;
331    for (i = 1; i < argc; i++) {
332       if (argv[i][0] != '-') {
333          clientname = argv[i];
334          break;
335       }
336       if (0 == strcmp(argv[i], "--")) {
337          if (i+1 < argc)
338             clientname = argv[i+1];
339          break;
340       }
341       if (0 == strcmp(argv[i], "-d"))
342          loglevel++;
343       if (0 == strncmp(argv[i], "--tool=", 7))
344          toolname = argv[i] + 7;
345    }
346 
347    /* ... and start the debug logger.  Now we can safely emit logging
348       messages all through startup. */
349    VG_(debugLog_startup)(loglevel, "Stage 1");
350 
351    /* Make sure we know which tool we're using */
352    if (toolname) {
353       VG_(debugLog)(1, "launcher", "tool '%s' requested\n", toolname);
354    } else {
355       VG_(debugLog)(1, "launcher",
356                        "no tool requested, defaulting to 'memcheck'\n");
357       toolname = "memcheck";
358    }
359 
360    /* Select a platform to use if we can't decide that by looking at
361       the executable (eg because it's a shell script).  VG_PLATFORM is the
362       default_platform. Its value is defined in coregrind/Makefile.am and
363       typically it is the primary build target. Unless the primary build
364       target is not built is not built in which case VG_PLATFORM is the
365       secondary build target. */
366 #  if defined(VGO_linux)
367    if ((0==strcmp(VG_PLATFORM,"x86-linux"))    ||
368        (0==strcmp(VG_PLATFORM,"amd64-linux"))  ||
369        (0==strcmp(VG_PLATFORM,"ppc32-linux"))  ||
370        (0==strcmp(VG_PLATFORM,"ppc64be-linux"))  ||
371        (0==strcmp(VG_PLATFORM,"ppc64le-linux"))  ||
372        (0==strcmp(VG_PLATFORM,"arm-linux"))    ||
373        (0==strcmp(VG_PLATFORM,"arm64-linux"))  ||
374        (0==strcmp(VG_PLATFORM,"s390x-linux"))  ||
375        (0==strcmp(VG_PLATFORM,"tilegx-linux")) ||
376        (0==strcmp(VG_PLATFORM,"mips32-linux")) ||
377        (0==strcmp(VG_PLATFORM,"mips64-linux")))
378       default_platform = VG_PLATFORM;
379 #  elif defined(VGO_solaris)
380    if ((0==strcmp(VG_PLATFORM,"x86-solaris")) ||
381        (0==strcmp(VG_PLATFORM,"amd64-solaris")))
382       default_platform = SOLARIS_LAUNCHER_DEFAULT_PLATFORM;
383 #  else
384 #    error Unknown OS
385 #  endif
386    else
387       barf("Unknown VG_PLATFORM '%s'", VG_PLATFORM);
388 
389    /* Work out what platform to use, or use the default platform if
390       not possible. */
391    if (clientname == NULL) {
392       VG_(debugLog)(1, "launcher",
393                        "no client specified, defaulting platform to '%s'\n",
394                         default_platform);
395       platform = default_platform;
396    } else if ((platform = select_platform(clientname)) != NULL) {
397       VG_(debugLog)(1, "launcher", "selected platform '%s'\n", platform);
398    } else {
399       VG_(debugLog)(1, "launcher",
400                        "no platform detected, defaulting platform to '%s'\n",
401                        default_platform);
402       platform = default_platform;
403    }
404 
405    /* Figure out the name of this executable (viz, the launcher), so
406       we can tell stage2.  stage2 will use the name for recursive
407       invocations of valgrind on child processes. */
408 #  if defined(VGO_linux)
409    linkname = "/proc/self/exe";
410 #  elif defined(VGO_solaris)
411    linkname = "/proc/self/path/a.out";
412 #  else
413 #    error Unknown OS
414 #  endif
415    unsigned bufsiz = 0;
416    char *buf = NULL;
417 
418    while (42) {
419       bufsiz += 500;
420       buf = realloc(buf, bufsiz);
421       if (buf == NULL)
422          barf("realloc of buf failed.");
423       r = readlink(linkname, buf, bufsiz);
424       if (r == -1) {
425         /* If /proc/self/exe (/proc/self/path/a.out) can't be followed, don't
426            give up. Instead continue with an empty string for VALGRIND_LAUNCHER.
427            In the sys_execve wrapper, this is tested, and if found to be empty,
428            fail the execve. */
429         fprintf(stderr, "valgrind: warning (non-fatal): "
430                 "readlink(\"%s\") failed.\n", linkname);
431         fprintf(stderr, "valgrind: continuing, however --trace-children=yes "
432                 "will not work.\n");
433         launcher_name = "";
434         break;
435       }
436       if (r == bufsiz) continue;   // buffer to small; retry
437 
438       assert(r < bufsiz);   // paranoia
439 
440       buf[r] = '\0';
441       launcher_name = buf;
442       break;
443    }
444 
445    /* tediously augment the env: VALGRIND_LAUNCHER=launcher_name */
446    new_line = malloc(strlen(VALGRIND_LAUNCHER) + 1
447                      + strlen(launcher_name) + 1);
448    if (new_line == NULL)
449       barf("malloc of new_line failed.");
450    strcpy(new_line, VALGRIND_LAUNCHER);
451    strcat(new_line, "=");
452    strcat(new_line, launcher_name);
453 
454    for (j = 0; envp[j]; j++)
455       ;
456    new_env = malloc((j+2) * sizeof(char*));
457    if (new_env == NULL)
458       barf("malloc of new_env failed.");
459    for (i = 0; i < j; i++)
460       new_env[i] = envp[i];
461    new_env[i++] = new_line;
462    new_env[i++] = NULL;
463    assert(i == j+2);
464 
465    /* Establish the correct VALGRIND_LIB. */
466    cp = getenv(VALGRIND_LIB);
467 
468    if (cp != NULL)
469       valgrind_lib = cp;
470 
471    /* Build the stage2 invocation, and execve it.  Bye! */
472    toolfile = malloc(strlen(valgrind_lib) + strlen(toolname) + strlen(platform) + 3);
473    if (toolfile == NULL)
474       barf("malloc of toolfile failed.");
475    sprintf(toolfile, "%s/%s-%s", valgrind_lib, toolname, platform);
476 
477    VG_(debugLog)(1, "launcher", "launching %s\n", toolfile);
478 
479    execve(toolfile, argv, new_env);
480 
481    fprintf(stderr, "valgrind: failed to start tool '%s' for platform '%s': %s\n",
482                    toolname, platform, strerror(errno));
483 
484    exit(1);
485 }
486