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