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