xref: /netbsd-src/external/gpl3/binutils.old/dist/gprofng/src/checks.cc (revision c42dbd0ed2e61fe6eda8590caa852ccf34719964)
1 /* Copyright (C) 2021 Free Software Foundation, Inc.
2    Contributed by Oracle.
3 
4    This file is part of GNU Binutils.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 51 Franklin Street - Fifth Floor, Boston,
19    MA 02110-1301, USA.  */
20 
21 #include "config.h"
22 #include <ctype.h>
23 #include <unistd.h>
24 #include <sys/utsname.h>
25 #include <sys/param.h>
26 
27 #include "gp-defs.h"
28 #include "Elf.h"
29 #include "collctrl.h"
30 #include "i18n.h"
31 #include "util.h"
32 #include "collect.h"
33 
34 void
check_target(int argc,char ** argv)35 collect::check_target (int argc, char **argv)
36 {
37   char *next;
38   char *last = 0;
39   char *a;
40   char *ccret;
41   char **lasts = &last;
42   int tindex = targ_index;
43   int ret;
44   char *basename;
45   is_64 = false;
46 
47   /* now check the executable */
48   nargs = argc - targ_index;
49   Exec_status rv = check_executable (argv[targ_index]);
50   switch (rv)
51     {
52     case EXEC_OK:
53       njargs = cc->get_java_arg_cnt ();
54       arglist = (char **) calloc (nargs + 5 + njargs, sizeof (char *));
55       jargs = cc->get_java_args ();
56 
57       // store the first argument -- target name
58       ret = 0;
59       arglist[ret++] = argv[tindex++];
60       if (cc->get_java_mode () == 1)
61 	{
62 	  // add any user-specified -J (Java) arguments
63 	  int length = (int) strlen (argv[targ_index]);
64 	  int is_java = 0;
65 	  if ((length >= 6) && strcmp (&argv[targ_index][length - 5], NTXT ("/java")) == 0)
66 	    is_java = 1;
67 	  else if ((length == 4) && strcmp (&argv[targ_index][0], NTXT ("java")) == 0)
68 	    is_java = 1;
69 	  if (njargs != 0 && is_java)
70 	    {
71 	      next = strtok_r (jargs, NTXT (" \t"), lasts);
72 	      arglist[ret++] = next;
73 	      for (;;)
74 		{
75 		  next = strtok_r (NULL, NTXT (" \t"), lasts);
76 		  if (next == NULL)
77 		    break;
78 		  arglist[ret++] = next;
79 		}
80 	    }
81 	}
82 
83       // copy the rest of the arguments
84       for (int i = 1; i < nargs; i++)
85 	arglist[ret++] = argv[tindex++];
86       nargs = ret;
87       break;
88     case EXEC_IS_JAR:
89       // Preface the user-supplied argument list with
90       //	the path to the java, the collector invocation,
91       //	any -J java arguments provided, and "-jar".
92       ccret = cc->set_java_mode (NTXT ("on"));
93       if (ccret != NULL)
94 	{
95 	  writeStr (2, ccret);
96 	  exit (1);
97 	}
98       njargs = cc->get_java_arg_cnt ();
99       arglist = (char **) calloc (nargs + 5 + njargs, sizeof (char *));
100       jargs = cc->get_java_args ();
101 
102       a = find_java ();
103       if (a == NULL)
104 	exit (1); // message was written
105       ret = 0;
106       arglist[ret++] = a;
107       // add any user-specified Java arguments
108       if (njargs != 0)
109 	{
110 	  next = strtok_r (jargs, NTXT (" \t"), lasts);
111 	  arglist[ret++] = next;
112 	  for (;;)
113 	    {
114 	      next = strtok_r (NULL, NTXT (" \t"), lasts);
115 	      if (next == NULL)
116 		break;
117 	      arglist[ret++] = next;
118 	    }
119 	}
120       arglist[ret++] = NTXT ("-jar");
121       for (int i = 0; i < nargs; i++)
122 	arglist[ret++] = argv[tindex++];
123       nargs = ret;
124       break;
125     case EXEC_IS_CLASSCLASS:
126       // remove the .class from the name
127       ret = (int) strlen (argv[targ_index]);
128       argv[targ_index][ret - 6] = 0;
129 
130       // now fall through to the EXEC_IS_CLASS case
131     case EXEC_IS_CLASS:
132       // Preface the user-supplied argument list with
133       //	the path to the java, the collector invocation,
134       //	and any -J java arguments provided.
135       ccret = cc->set_java_mode (NTXT ("on"));
136       if (ccret != NULL)
137 	{
138 	  writeStr (2, ccret);
139 	  exit (1);
140 	}
141       jargs = cc->get_java_args ();
142       njargs = cc->get_java_arg_cnt ();
143       arglist = (char **) calloc (nargs + 4 + njargs, sizeof (char *));
144 
145       a = find_java ();
146       if (a == NULL)
147 	exit (1); // message was written
148       ret = 0;
149       arglist[ret++] = a;
150       // add any user-specified Java arguments
151       if (njargs != 0)
152 	{
153 	  next = strtok_r (jargs, NTXT (" \t"), lasts);
154 	  arglist[ret++] = next;
155 	  for (;;)
156 	    {
157 	      next = strtok_r (NULL, NTXT (" \t"), lasts);
158 	      if (next == NULL)
159 		break;
160 	      arglist[ret++] = next;
161 	    }
162 	}
163 
164       // copy the remaining arguments to the new list
165       for (int i = 0; i < nargs; i++)
166 	arglist[ret++] = argv[tindex++];
167       nargs = ret;
168       break;
169     case EXEC_ELF_NOSHARE:
170     case EXEC_OPEN_FAIL:
171     case EXEC_ELF_LIB:
172     case EXEC_ELF_HEADER:
173     case EXEC_ELF_ARCH:
174     case EXEC_ISDIR:
175     case EXEC_NOT_EXEC:
176     case EXEC_NOT_FOUND:
177     default:
178       /* something wrong; write a message */
179       char *errstr = status_str (rv, argv[targ_index]);
180       if (errstr)
181 	{
182 	  dbe_write (2, "%s", errstr);
183 	  free (errstr);
184 	}
185       exit (1);
186     }
187   cc->set_target (arglist[0]);
188 
189   /* check the experiment */
190   char *ccwarn;
191   ccret = cc->check_expt (&ccwarn);
192   if (ccwarn != NULL)
193     {
194       writeStr (2, ccwarn);
195       free (ccwarn);
196     }
197   if (ccret != NULL)
198     {
199       writeStr (2, ccret);
200       exit (1);
201     }
202   /* check if java, to see if -j flag was given */
203   if ((basename = strrchr (arglist[0], '/')) == NULL)
204     basename = arglist[0];
205   else
206     basename++;
207   if (strcmp (basename, NTXT ("java")) == 0)
208     {
209       /* the target's name is java; was java flag set? */
210       if ((jseen_global == 0) && (cc->get_java_mode () == 0))
211 	{
212 	  char *cret = cc->set_java_mode (NTXT ("on"));
213 	  if (cret != NULL)
214 	    {
215 	      writeStr (2, cret);
216 	      exit (1);
217 	    }
218 	}
219     }
220 }
221 
222 collect::Exec_status
check_executable(char * target_name)223 collect::check_executable (char *target_name)
224 {
225   char target_path[MAXPATHLEN];
226   struct stat64 statbuf;
227   if (target_name == NULL) // not set, but assume caller knows what it's doing
228     return EXEC_OK;
229   if (getenv ("GPROFNG_SKIP_VALIDATION")) // don't check target
230     return EXEC_OK;
231 
232   // see if target exists and is not a directory
233   if ((dbe_stat (target_name, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFDIR))
234     {
235       // target is found, check for access as executable
236       if (access (target_name, X_OK) != 0)
237 	{
238 	  // not an executable, check for jar or class file
239 	  int i = (int) strlen (target_name);
240 	  if ((i >= 5) && strcmp (&target_name[i - 4], NTXT (".jar")) == 0)
241 	    {
242 	      // could be a jar file
243 	      // XXXX -- need better check for real jar file
244 	      cc->set_java_mode ("on");
245 	      return EXEC_IS_JAR;
246 	    }
247 	  if ((i >= 7) && strcmp (&target_name[i - 6], NTXT (".class")) == 0)
248 	    {
249 	      // could be a class file
250 	      // XXXX -- need better check for real class file
251 	      cc->set_java_mode (NTXT ("on"));
252 	      return EXEC_IS_CLASSCLASS;
253 	    }
254 	  // not a jar or class file, return not an executable
255 	  return EXEC_NOT_EXEC;
256 	}
257       else  // found, and it is executable. set the path to it
258 	snprintf (target_path, sizeof (target_path), NTXT ("%s"), target_name);
259     }
260   else
261     {
262       // not found, look on path
263       char *exe_name = get_realpath (target_name);
264       if (access (exe_name, X_OK) == 0)
265 	{
266 	  // target can't be located
267 	  // one last attempt: append .class to name, and see if we can find it
268 	  snprintf (target_path, sizeof (target_path), NTXT ("%s.class"), target_name);
269 	  if (dbe_stat (target_path, &statbuf) == 0)
270 	    {
271 	      // the file exists
272 	      if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
273 		{
274 		  // this is a directory; that won't do.
275 		  return EXEC_ISDIR;
276 		}
277 	      // say it's a class file
278 	      cc->set_java_mode (NTXT ("on"));
279 	      return EXEC_IS_CLASS;
280 	    }
281 	  return EXEC_NOT_FOUND;
282 	}
283       snprintf (target_path, sizeof (target_path), NTXT ("%s"), exe_name);
284       delete exe_name;
285     }
286 
287   // target_path is now the purported executable
288   // check for ELF library out of date
289   if (Elf::elf_version (EV_CURRENT) == EV_NONE)
290     return EXEC_ELF_LIB;
291   Elf *elf = Elf::elf_begin (target_path);
292   if (elf == NULL)
293     return EXEC_OK;
294   // do not by pass checking architectural match
295   collect::Exec_status exec_stat = check_executable_arch (elf);
296   if (exec_stat != EXEC_OK)
297     {
298       delete elf;
299       return exec_stat;
300     }
301   delete elf;
302   return EXEC_OK;
303 }
304 
305 collect::Exec_status
check_executable_arch(Elf * elf)306 collect::check_executable_arch (Elf *elf)
307 {
308   Elf_Internal_Ehdr *ehdrp = elf->elf_getehdr ();
309   if (ehdrp == NULL)
310     return EXEC_ELF_HEADER;
311   unsigned short machine = ehdrp->e_machine;
312 
313   switch (machine)
314     {
315 #if ARCH(SPARC)
316     case EM_SPARC:
317     case EM_SPARC32PLUS:
318       break;
319     case EM_SPARCV9:
320       is_64 = true;
321       break;
322 #elif ARCH(Intel)
323     case EM_X86_64:
324       {
325 	is_64 = true;
326 	// now figure out if the platform can run it
327 	struct utsname unbuf;
328 	int r = uname (&unbuf);
329 	if (r == 0 && unbuf.machine && strstr (unbuf.machine, "_64") == NULL)
330 	  // machine can not run 64 bits, but this code is 64-bit
331 	  return EXEC_ELF_ARCH;
332       }
333       break;
334     case EM_386:
335       break;
336 #elif ARCH(Aarch64)
337     case EM_AARCH64:
338       is_64 = true;
339       break;
340 #endif
341     default:
342       return EXEC_ELF_ARCH;
343     }
344 
345   // now check if target was built with shared libraries
346   int dynamic = 0;
347   for (unsigned cnt = 0; cnt < ehdrp->e_phnum; cnt++)
348     {
349       Elf_Internal_Phdr *phdrp = elf->get_phdr (cnt);
350       if (phdrp && phdrp->p_type == PT_DYNAMIC)
351 	{
352 	  dynamic = 1;
353 	  break;
354 	}
355     }
356   if (dynamic == 0)
357     {
358       // target is not a dynamic executable or shared object;
359       // can't record data
360       return EXEC_ELF_NOSHARE;
361     }
362   return EXEC_OK;
363 }
364 
365 char *
status_str(Exec_status rv,char * target_name)366 collect::status_str (Exec_status rv, char *target_name)
367 {
368   switch (rv)
369     {
370     case EXEC_OK:
371     case EXEC_IS_JAR:
372     case EXEC_IS_CLASS:
373     case EXEC_IS_CLASSCLASS:
374       // supported flavors -- no error message
375       return NULL;
376     case EXEC_ELF_NOSHARE:
377       return dbe_sprintf (GTXT ("Target executable `%s' must be built with shared libraries\n"), target_name);
378     case EXEC_OPEN_FAIL:
379       return dbe_sprintf (GTXT ("Can't open target executable `%s'\n"), target_name);
380     case EXEC_ELF_LIB:
381       return strdup (GTXT ("Internal error: Not a working version of ELF library\n"));
382     case EXEC_ELF_HEADER:
383       return dbe_sprintf (GTXT ("Target `%s' is not a valid ELF executable\n"), target_name);
384     case EXEC_ELF_ARCH:
385       return dbe_sprintf (GTXT ("Target architecture of executable `%s' is not supported on this machine\n"), target_name);
386     case EXEC_ISDIR:
387       return dbe_sprintf (GTXT ("Target `%s' is a directory, not an executable\n"), target_name);
388     case EXEC_NOT_EXEC:
389       return dbe_sprintf (GTXT ("Target `%s' is not executable\n"), target_name);
390     case EXEC_NOT_FOUND:
391       return dbe_sprintf (GTXT ("Target `%s' not found\n"), target_name);
392     }
393   return NULL;
394 }
395 
396 char *
find_java(void)397 collect::find_java (void)
398 {
399   char buf[MAXPATHLEN];
400   char *var = NULL;
401   Exec_status rv = EXEC_OK;
402 
403   // first see if the user entered a -j argument
404   var = cc->get_java_path ();
405   if (var != NULL)
406     {
407       snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
408       java_how = NTXT ("-j");
409       rv = check_executable (buf);
410     }
411   // then try JDK_HOME
412   if (java_how == NULL)
413     {
414       var = getenv (NTXT ("JDK_HOME"));
415       if ((var != NULL) && (strlen (var) > 0))
416 	{
417 	  snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
418 	  java_how = NTXT ("JDK_HOME");
419 	  rv = check_executable (buf);
420 	}
421     }
422   // then try JAVA_PATH
423   if (java_how == NULL)
424     {
425       var = getenv (NTXT ("JAVA_PATH"));
426       if ((var != NULL) && (strlen (var) > 0))
427 	{
428 	  snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
429 	  java_how = NTXT ("JAVA_PATH");
430 	  rv = check_executable (buf);
431 	}
432     }
433   // try the user's path
434   if (java_how == NULL)
435     {
436       snprintf (buf, sizeof (buf), NTXT ("java"));
437       rv = check_executable (buf);
438       if (rv == EXEC_OK)
439 	java_how = NTXT ("PATH");
440     }
441   // finally, just try /usr/java -- system default
442   if (java_how == NULL)
443     {
444       snprintf (buf, sizeof (buf), NTXT ("/usr/java/bin/java"));
445       rv = check_executable (buf);
446       java_how = NTXT ("/usr/java/bin/java");
447     }
448 
449   // we now have a nominal path to java, and how we chose it
450   // and we have rv set to the check_executable return
451   switch (rv)
452     {
453     case EXEC_OK:
454       java_path = strdup (buf);
455       if (verbose == 1)
456 	dbe_write (2, GTXT ("Path to `%s' (set from %s) used for Java profiling\n"),
457 		   java_path, java_how);
458       return ( strdup (buf));
459     default:
460       dbe_write (2, GTXT ("Path to `%s' (set from %s) does not point to a JVM executable\n"),
461 		 buf, java_how);
462       break;
463     }
464   return NULL;
465 }
466 
467 void
validate_config(int how)468 collect::validate_config (int how)
469 {
470   if (getenv (NTXT ("GPROFNG_SKIP_VALIDATION")) != NULL)
471     return;
472   char *cmd = dbe_sprintf (NTXT ("%s/perftools_validate"), run_dir);
473   if (access (cmd, X_OK) != 0)
474     {
475       if (how)
476 	dbe_write (2, GTXT ("WARNING: Unable to validate system: `%s' could not be executed\n"), cmd);
477       return;
478     }
479   char *quiet = how == 0 ? NTXT ("") : NTXT ("-q"); // check collection, verbosely
480   char *buf;
481   if (cc->get_java_default () == 0 && java_path)
482     buf = dbe_sprintf (NTXT ("%s -c -j %s -H \"%s\" %s"), cmd, java_path, java_how, quiet);
483   else  // not java mode -- don't check the java version
484     buf = dbe_sprintf (NTXT ("%s -c %s"), cmd, quiet);
485   free (cmd);
486 
487   /* now run the command */
488   int ret = system (buf);
489   int status = WEXITSTATUS (ret);
490   if ((status & 0x1) != 0)
491     dbe_write (2, GTXT ("WARNING: Data collection may fail: system is not properly configured or is unsupported.\n"));
492   if ((status & 0x2) != 0)
493     dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n"));
494   free (buf);
495 }
496 
497 void
validate_java(const char * jvm,const char * jhow,int q)498 collect::validate_java (const char *jvm, const char *jhow, int q)
499 {
500   char *cmd = dbe_sprintf (NTXT ("%s/perftools_ckjava"), run_dir);
501   if (access (cmd, X_OK) != 0)
502     {
503       dbe_write (2, GTXT ("WARNING: Unable to validate Java: `%s' could not be executed\n"), cmd);
504       return;
505     }
506   char *buf = dbe_sprintf (NTXT ("%s -j %s -H \"%s\" %s"), cmd, jvm, jhow,
507 			   (q == 1 ? "-q" : ""));
508   free (cmd);
509 
510   /* now run the command */
511   int ret = system (buf);
512   int status = WEXITSTATUS (ret);
513   if (status != 0)
514     dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n"));
515   free (buf);
516 }
517