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 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 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 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 * 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 * 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 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 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