launcher-linux.c revision 663860b1408516d02ebfcb3a9999a134e6cfb223
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@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))
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   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 */
113static 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 */
237static const char *valgrind_lib = VG_LIBDIR;
238
239int 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