xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/src/checks.cc (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1 /* Copyright (C) 2021-2024 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
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
223 collect::check_executable (char *target_name)
224 {
225   char target_path[MAXPATHLEN];
226   dbe_stat_t 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   delete elf;
297   return exec_stat;
298 }
299 
300 collect::Exec_status
301 collect::check_executable_arch (Elf *elf)
302 {
303   Elf_Internal_Ehdr *ehdrp = elf->elf_getehdr ();
304   if (ehdrp == NULL)
305     return EXEC_ELF_HEADER;
306   unsigned short machine = ehdrp->e_machine;
307 
308   switch (machine)
309     {
310 #if ARCH(SPARC)
311     case EM_SPARC:
312     case EM_SPARC32PLUS:
313       break;
314     case EM_SPARCV9:
315       is_64 = true;
316       break;
317 #elif ARCH(Intel)
318     case EM_X86_64:
319       {
320 	is_64 = true;
321 	// now figure out if the platform can run it
322 	struct utsname unbuf;
323 	int r = uname (&unbuf);
324 	if (r == 0 && strstr (unbuf.machine, "_64") == NULL)
325 	  // machine can not run 64 bits, but this code is 64-bit
326 	  return EXEC_ELF_ARCH;
327       }
328       break;
329     case EM_386:
330       break;
331 #elif ARCH(Aarch64)
332     case EM_AARCH64:
333       is_64 = true;
334       break;
335 #endif
336     default:
337       return EXEC_ELF_ARCH;
338     }
339 
340   // now check if target was built with shared libraries
341   int dynamic = 0;
342   for (unsigned cnt = 0; cnt < ehdrp->e_phnum; cnt++)
343     {
344       Elf_Internal_Phdr *phdrp = elf->get_phdr (cnt);
345       if (phdrp && phdrp->p_type == PT_DYNAMIC)
346 	{
347 	  dynamic = 1;
348 	  break;
349 	}
350     }
351   if (dynamic == 0)
352     {
353       // target is not a dynamic executable or shared object;
354       // can't record data
355       return EXEC_ELF_NOSHARE;
356     }
357   return EXEC_OK;
358 }
359 
360 char *
361 collect::status_str (Exec_status rv, char *target_name)
362 {
363   switch (rv)
364     {
365     case EXEC_OK:
366     case EXEC_IS_JAR:
367     case EXEC_IS_CLASS:
368     case EXEC_IS_CLASSCLASS:
369       // supported flavors -- no error message
370       return NULL;
371     case EXEC_ELF_NOSHARE:
372       return dbe_sprintf (GTXT ("Target executable `%s' must be built with shared libraries\n"), target_name);
373     case EXEC_OPEN_FAIL:
374       return dbe_sprintf (GTXT ("Can't open target executable `%s'\n"), target_name);
375     case EXEC_ELF_LIB:
376       return strdup (GTXT ("Internal error: Not a working version of ELF library\n"));
377     case EXEC_ELF_HEADER:
378       return dbe_sprintf (GTXT ("Target `%s' is not a valid ELF executable\n"), target_name);
379     case EXEC_ELF_ARCH:
380       return dbe_sprintf (GTXT ("Target architecture of executable `%s' is not supported on this machine\n"), target_name);
381     case EXEC_ISDIR:
382       return dbe_sprintf (GTXT ("Target `%s' is a directory, not an executable\n"), target_name);
383     case EXEC_NOT_EXEC:
384       return dbe_sprintf (GTXT ("Target `%s' is not executable\n"), target_name);
385     case EXEC_NOT_FOUND:
386       return dbe_sprintf (GTXT ("Target `%s' not found\n"), target_name);
387     }
388   return NULL;
389 }
390 
391 char *
392 collect::find_java (void)
393 {
394   char buf[MAXPATHLEN];
395   char *var = NULL;
396   Exec_status rv = EXEC_OK;
397 
398   // first see if the user entered a -j argument
399   var = cc->get_java_path ();
400   if (var != NULL)
401     {
402       snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
403       java_how = NTXT ("-j");
404       rv = check_executable (buf);
405     }
406   // then try JDK_HOME
407   if (java_how == NULL)
408     {
409       var = getenv (NTXT ("JDK_HOME"));
410       if ((var != NULL) && (strlen (var) > 0))
411 	{
412 	  snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
413 	  java_how = NTXT ("JDK_HOME");
414 	  rv = check_executable (buf);
415 	}
416     }
417   // then try JAVA_PATH
418   if (java_how == NULL)
419     {
420       var = getenv (NTXT ("JAVA_PATH"));
421       if ((var != NULL) && (strlen (var) > 0))
422 	{
423 	  snprintf (buf, sizeof (buf), NTXT ("%s/bin/java"), var);
424 	  java_how = NTXT ("JAVA_PATH");
425 	  rv = check_executable (buf);
426 	}
427     }
428   // try the user's path
429   if (java_how == NULL)
430     {
431       snprintf (buf, sizeof (buf), NTXT ("java"));
432       rv = check_executable (buf);
433       if (rv == EXEC_OK)
434 	java_how = NTXT ("PATH");
435     }
436   // finally, just try /usr/java -- system default
437   if (java_how == NULL)
438     {
439       snprintf (buf, sizeof (buf), NTXT ("/usr/java/bin/java"));
440       rv = check_executable (buf);
441       java_how = NTXT ("/usr/java/bin/java");
442     }
443 
444   // we now have a nominal path to java, and how we chose it
445   // and we have rv set to the check_executable return
446   switch (rv)
447     {
448     case EXEC_OK:
449       java_path = strdup (buf);
450       if (verbose == 1)
451 	dbe_write (2, GTXT ("Path to `%s' (set from %s) used for Java profiling\n"),
452 		   java_path, java_how);
453       return ( strdup (buf));
454     default:
455       dbe_write (2, GTXT ("Path to `%s' (set from %s) does not point to a JVM executable\n"),
456 		 buf, java_how);
457       break;
458     }
459   return NULL;
460 }
461 
462 void
463 collect::validate_config (int how)
464 {
465   if (getenv (NTXT ("GPROFNG_SKIP_VALIDATION")) != NULL)
466     return;
467   char *cmd = dbe_sprintf (NTXT ("%s/perftools_validate"), run_dir);
468   if (access (cmd, X_OK) != 0)
469     {
470       if (how)
471 	dbe_write (2, GTXT ("WARNING: Unable to validate system: `%s' could not be executed\n"), cmd);
472       return;
473     }
474   char *quiet = how == 0 ? NTXT ("") : NTXT ("-q"); // check collection, verbosely
475   char *buf;
476   if (cc->get_java_default () == 0 && java_path)
477     buf = dbe_sprintf (NTXT ("%s -c -j %s -H \"%s\" %s"), cmd, java_path, java_how, quiet);
478   else  // not java mode -- don't check the java version
479     buf = dbe_sprintf (NTXT ("%s -c %s"), cmd, quiet);
480   free (cmd);
481 
482   /* now run the command */
483   int ret = system (buf);
484   int status = WEXITSTATUS (ret);
485   if ((status & 0x1) != 0)
486     dbe_write (2, GTXT ("WARNING: Data collection may fail: system is not properly configured or is unsupported.\n"));
487   if ((status & 0x2) != 0)
488     dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n"));
489   free (buf);
490 }
491 
492 void
493 collect::validate_java (const char *jvm, const char *jhow, int q)
494 {
495   char *cmd = dbe_sprintf (NTXT ("%s/perftools_ckjava"), run_dir);
496   if (access (cmd, X_OK) != 0)
497     {
498       dbe_write (2, GTXT ("WARNING: Unable to validate Java: `%s' could not be executed\n"), cmd);
499       return;
500     }
501   char *buf = dbe_sprintf (NTXT ("%s -j %s -H \"%s\" %s"), cmd, jvm, jhow,
502 			   (q == 1 ? "-q" : ""));
503   free (cmd);
504 
505   /* now run the command */
506   int ret = system (buf);
507   int status = WEXITSTATUS (ret);
508   if (status != 0)
509     dbe_write (2, GTXT ("WARNING: Java data collection may fail: J2SE[tm] version is unsupported.\n"));
510   free (buf);
511 }
512