1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1988 AT&T 24 * Copyright (c) 1989 AT&T 25 * All Rights Reserved 26 * 27 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 28 * Copyright 2018 Jason King 29 */ 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <ctype.h> 35 #include <locale.h> 36 #include <libelf.h> 37 #include <sys/elf_SPARC.h> 38 39 40 /* exit return codes */ 41 #define NOARGS 1 42 #define BADELF 2 43 #define NOALLOC 3 44 45 #include <fcntl.h> 46 #include <sys/stat.h> 47 #include <errno.h> 48 #include <string.h> 49 #include <dlfcn.h> 50 51 #include "sgs.h" 52 #include "conv.h" 53 #include "gelf.h" 54 55 typedef struct { /* structure to translate symbol table data */ 56 int indx; 57 char *name; 58 GElf_Addr value; 59 GElf_Xword size; 60 int type; 61 int bind; 62 unsigned char other; 63 unsigned int shndx; 64 unsigned int flags; /* flags relevant to entry */ 65 } SYM; 66 67 #define FLG_SYM_SPECSEC 0x00000001 /* reserved scn index */ 68 /* (SHN_ABS, SHN_COMMON, ...) */ 69 70 #define UNDEFINED "U" 71 #define BSS_GLOB "B" 72 #define BSS_WEAK "B*" 73 #define BSS_LOCL "b" 74 #define BSS_SECN ".bss" 75 #define REG_GLOB "R" 76 #define REG_WEAK "R*" 77 #define REG_LOCL "r" 78 79 #define OPTSTR ":APDoxhvnursplLCVefgRTt:" /* option string for getopt() */ 80 81 #define DATESIZE 60 82 83 #define TYPE 7 84 #define BIND 3 85 86 #define DEF_MAX_SYM_SIZE 256 87 88 static char *key[TYPE][BIND]; 89 90 /* 91 * Format type used for printing value and size items. 92 * The non-negative values here are used as array indices into 93 * several arrays found below. Renumbering, or adding items, 94 * will require changes to those arrays as well. 95 */ 96 typedef enum { 97 FMT_T_NONE = -1, /* No format type yet assigned */ 98 99 /* The following are used as array indices */ 100 FMT_T_DEC = 0, 101 FMT_T_HEX = 1, 102 FMT_T_OCT = 2 103 } FMT_T; 104 105 /* 106 * Determine whether a proposed format type is compatible with the current 107 * setting. We allow setting the format as long as it hasn't already 108 * been done, or if the new setting is the same as the current one. 109 */ 110 #define COMPAT_FMT_FLAG(new_fmt_flag) \ 111 (fmt_flag == FMT_T_NONE) || (fmt_flag == new_fmt_flag) 112 113 static FMT_T fmt_flag = FMT_T_NONE; /* format style to use for value/size */ 114 115 static int /* flags: ?_flag corresponds to ? option */ 116 h_flag = 0, /* suppress printing of headings */ 117 v_flag = 0, /* sort external symbols by value */ 118 n_flag = 0, /* sort external symbols by name */ 119 u_flag = 0, /* print only undefined symbols */ 120 r_flag = 0, /* prepend object file or archive name */ 121 /* to each symbol name */ 122 R_flag = 0, /* if "-R" issued then prepend archive name, */ 123 /* object file name to each symbol */ 124 s_flag = 0, /* print section name instead of section index */ 125 p_flag = 0, /* produce terse output */ 126 P_flag = 0, /* Portable format output */ 127 l_flag = 0, /* produce long listing of output */ 128 L_flag = 0, /* print SUNW_LDYNSYM instead of SYMTAB */ 129 D_flag = 0, /* print DYNSYM instead of SYMTAB */ 130 C_flag = 0, /* print decoded C++ names */ 131 A_flag = 0, /* File name */ 132 e_flag = 0, /* -e flag */ 133 g_flag = 0, /* -g flag */ 134 V_flag = 0; /* print version information */ 135 static char A_header[DEF_MAX_SYM_SIZE+1] = {0}; 136 137 static char *prog_name; 138 static char *archive_name = (char *)0; 139 static int errflag = 0; 140 static void usage(); 141 static void each_file(char *); 142 static void process(Elf *, char *); 143 static Elf_Scn * get_scnfd(Elf *, int, int); 144 static void get_symtab(Elf *, char *); 145 static SYM * readsyms(Elf_Data *, GElf_Sxword, Elf *, unsigned int, 146 unsigned int); 147 static int compare(SYM *, SYM *); 148 static char *lookup(int, int); 149 static int is_bss_section(unsigned int, Elf *, unsigned int); 150 static void print_ar_files(int, Elf *, char *); 151 static void print_symtab(Elf *, unsigned int, Elf_Scn *, GElf_Shdr *, char *); 152 static void parsename(char *); 153 static void parse_fn_and_print(const char *, char *); 154 static char d_buf[512]; 155 static char p_buf[512]; 156 static int exotic(const char *s); 157 static void set_A_header(char *); 158 static char *FormatName(char *, const char *); 159 160 161 162 /* 163 * Parses the command line options and then 164 * calls each_file() to process each file. 165 */ 166 int 167 main(int argc, char *argv[], char *envp[]) 168 { 169 char *optstr = OPTSTR; /* option string used by getopt() */ 170 int optchar; 171 FMT_T new_fmt_flag; 172 173 #ifndef XPG4 174 /* 175 * Check for a binary that better fits this architecture. 176 */ 177 (void) conv_check_native(argv, envp); 178 #endif 179 180 /* table of keyletters for use with -p and -P options */ 181 key[STT_NOTYPE][STB_LOCAL] = "n"; 182 key[STT_NOTYPE][STB_GLOBAL] = "N"; 183 key[STT_NOTYPE][STB_WEAK] = "N*"; 184 key[STT_OBJECT][STB_LOCAL] = "d"; 185 key[STT_OBJECT][STB_GLOBAL] = "D"; 186 key[STT_OBJECT][STB_WEAK] = "D*"; 187 key[STT_FUNC][STB_LOCAL] = "t"; 188 key[STT_FUNC][STB_GLOBAL] = "T"; 189 key[STT_FUNC][STB_WEAK] = "T*"; 190 key[STT_SECTION][STB_LOCAL] = "s"; 191 key[STT_SECTION][STB_GLOBAL] = "S"; 192 key[STT_SECTION][STB_WEAK] = "S*"; 193 key[STT_FILE][STB_LOCAL] = "f"; 194 key[STT_FILE][STB_GLOBAL] = "F"; 195 key[STT_FILE][STB_WEAK] = "F*"; 196 key[STT_COMMON][STB_LOCAL] = "c"; 197 key[STT_COMMON][STB_GLOBAL] = "C"; 198 key[STT_COMMON][STB_WEAK] = "C*"; 199 key[STT_TLS][STB_LOCAL] = "l"; 200 key[STT_TLS][STB_GLOBAL] = "L"; 201 key[STT_TLS][STB_WEAK] = "L*"; 202 203 prog_name = argv[0]; 204 205 (void) setlocale(LC_ALL, ""); 206 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 207 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 208 #endif 209 (void) textdomain(TEXT_DOMAIN); 210 211 while ((optchar = getopt(argc, argv, optstr)) != -1) { 212 switch (optchar) { 213 case 'o': if (COMPAT_FMT_FLAG(FMT_T_OCT)) 214 fmt_flag = FMT_T_OCT; 215 else 216 (void) fprintf(stderr, gettext( 217 "%s: -x or -t set, -o ignored\n"), 218 prog_name); 219 break; 220 case 'x': if (COMPAT_FMT_FLAG(FMT_T_HEX)) 221 fmt_flag = FMT_T_HEX; 222 else 223 (void) fprintf(stderr, gettext( 224 "%s: -o or -t set, -x ignored\n"), 225 prog_name); 226 break; 227 case 'h': h_flag = 1; 228 break; 229 case 'v': if (!n_flag) 230 v_flag = 1; 231 else 232 (void) fprintf(stderr, gettext( 233 "%s: -n set, -v ignored\n"), 234 prog_name); 235 break; 236 case 'n': if (!v_flag) 237 n_flag = 1; 238 else 239 (void) fprintf(stderr, gettext( 240 "%s: -v set, -n ignored\n"), 241 prog_name); 242 break; 243 case 'u': if (!e_flag && !g_flag) 244 u_flag = 1; 245 else 246 (void) fprintf(stderr, gettext( 247 "%s: -e or -g set, -u ignored\n"), 248 prog_name); 249 break; 250 case 'e': if (!u_flag && !g_flag) 251 e_flag = 1; 252 else 253 (void) fprintf(stderr, gettext( 254 "%s: -u or -g set, -e ignored\n"), 255 prog_name); 256 break; 257 case 'g': if (!u_flag && !e_flag) 258 g_flag = 1; 259 else 260 (void) fprintf(stderr, gettext( 261 "%s: -u or -e set, -g ignored\n"), 262 prog_name); 263 break; 264 case 'r': if (R_flag) { 265 R_flag = 0; 266 (void) fprintf(stderr, gettext( 267 "%s: -r set, -R ignored\n"), 268 prog_name); 269 } 270 r_flag = 1; 271 break; 272 case 's': s_flag = 1; 273 break; 274 case 'p': if (P_flag == 1) { 275 (void) fprintf(stderr, gettext( 276 "nm: -P set. -p ignored\n")); 277 } else 278 p_flag = 1; 279 break; 280 case 'P': if (p_flag == 1) { 281 (void) fprintf(stderr, gettext( 282 "nm: -p set. -P ignored\n")); 283 } else 284 P_flag = 1; 285 break; 286 case 'l': l_flag = 1; 287 break; 288 case 'L': if (D_flag == 1) { 289 (void) fprintf(stderr, gettext( 290 "nm: -D set. -L ignored\n")); 291 } else 292 L_flag = 1; 293 break; 294 case 'D': if (L_flag == 1) { 295 (void) fprintf(stderr, gettext( 296 "nm: -L set. -D ignored\n")); 297 } else 298 D_flag = 1; 299 break; 300 case 'C': 301 C_flag = 1; 302 break; 303 case 'A': A_flag = 1; 304 break; 305 case 'V': V_flag = 1; 306 (void) fprintf(stderr, "nm: %s %s\n", 307 (const char *)SGU_PKG, 308 (const char *)SGU_REL); 309 break; 310 case 'f': /* -f is a noop, see man page */ 311 break; 312 case 'R': if (!r_flag) 313 R_flag = 1; 314 else 315 (void) fprintf(stderr, gettext( 316 "%s: -r set, -R ignored\n"), 317 prog_name); 318 break; 319 case 'T': 320 break; 321 case 't': if (strcmp(optarg, "o") == 0) { 322 new_fmt_flag = FMT_T_OCT; 323 } else if (strcmp(optarg, "d") == 0) { 324 new_fmt_flag = FMT_T_DEC; 325 } else if (strcmp(optarg, "x") == 0) { 326 new_fmt_flag = FMT_T_HEX; 327 } else { 328 new_fmt_flag = FMT_T_NONE; 329 } 330 if (new_fmt_flag == FMT_T_NONE) { 331 errflag += 1; 332 (void) fprintf(stderr, gettext( 333 "nm: -t requires radix value (d, o, x): %s\n"), optarg); 334 } else if (COMPAT_FMT_FLAG(new_fmt_flag)) { 335 fmt_flag = new_fmt_flag; 336 } else { 337 (void) fprintf(stderr, gettext( 338 "nm: -t or -o or -x set. -t ignored.\n")); 339 } 340 break; 341 case ':': errflag += 1; 342 (void) fprintf(stderr, gettext( 343 "nm: %c requires operand\n"), optopt); 344 break; 345 case '?': errflag += 1; 346 break; 347 default: break; 348 } 349 } 350 351 if (errflag || (optind >= argc)) { 352 if (!(V_flag && (argc == 2))) { 353 usage(); 354 exit(NOARGS); 355 } 356 } 357 358 /* 359 * If no explicit format style was specified, set the default 360 * here. In general, the default is for value and size items 361 * to be displayed in decimal format. The exception is that 362 * the default for -P is hexidecimal. 363 */ 364 if (fmt_flag == FMT_T_NONE) 365 fmt_flag = P_flag ? FMT_T_HEX : FMT_T_DEC; 366 367 368 while (optind < argc) { 369 each_file(argv[optind]); 370 optind++; 371 } 372 return (errflag); 373 } 374 375 /* 376 * Print out a usage message in short form when program is invoked 377 * with insufficient or no arguments, and in long form when given 378 * either a ? or an invalid option. 379 */ 380 static void 381 usage() 382 { 383 (void) fprintf(stderr, gettext( 384 "Usage: nm [-ACDhLlnPpRrsTVv] [-efox] [-g | -u] [-t d|o|x] file ...\n")); 385 } 386 387 /* 388 * Takes a filename as input. Test first for a valid version 389 * of libelf.a and exit on error. Process each valid file 390 * or archive given as input on the command line. Check 391 * for file type. If it is an archive, call print_ar_files 392 * to process each member of the archive in the same manner 393 * as object files on the command line. The same tests for 394 * valid object file type apply to regular archive members. 395 * If it is an ELF object file, process it; otherwise 396 * warn that it is an invalid file type and return from 397 * processing the file. 398 */ 399 400 static void 401 each_file(char *filename) 402 { 403 Elf *elf_file; 404 int fd; 405 Elf_Kind file_type; 406 407 struct stat64 buf; 408 409 Elf_Cmd cmd; 410 errno = 0; 411 if (stat64(filename, &buf) == -1) { 412 (void) fprintf(stderr, "%s: ", prog_name); 413 perror(filename); 414 errflag++; 415 return; 416 } 417 if (elf_version(EV_CURRENT) == EV_NONE) { 418 (void) fprintf(stderr, gettext( 419 "%s: %s: libelf is out of date\n"), 420 prog_name, filename); 421 exit(BADELF); 422 } 423 424 if ((fd = open((filename), O_RDONLY)) == -1) { 425 (void) fprintf(stderr, gettext("%s: %s: cannot read file\n"), 426 prog_name, filename); 427 errflag++; 428 return; 429 } 430 cmd = ELF_C_READ; 431 if ((elf_file = elf_begin(fd, cmd, (Elf *) 0)) == NULL) { 432 (void) fprintf(stderr, 433 "%s: %s: %s\n", prog_name, filename, elf_errmsg(-1)); 434 errflag++; 435 (void) close(fd); 436 return; 437 } 438 file_type = elf_kind(elf_file); 439 if (file_type == ELF_K_AR) { 440 print_ar_files(fd, elf_file, filename); 441 } else { 442 if (file_type == ELF_K_ELF) { 443 #ifndef XPG4 444 if (u_flag && !h_flag) { 445 /* 446 * u_flag is specified. 447 */ 448 if (p_flag) 449 (void) printf("\n\n%s:\n\n", filename); 450 else 451 (void) printf(gettext( 452 "\n\nUndefined symbols from %s:\n\n"), 453 filename); 454 } else if (!h_flag & !P_flag) 455 #else 456 if (!h_flag & !P_flag) 457 #endif 458 { 459 if (p_flag) 460 (void) printf("\n\n%s:\n", filename); 461 else { 462 if (A_flag != 0) 463 (void) printf("\n\n%s%s:\n", 464 A_header, filename); 465 else 466 (void) printf("\n\n%s:\n", 467 filename); 468 } 469 } 470 archive_name = (char *)0; 471 process(elf_file, filename); 472 } else { 473 (void) fprintf(stderr, gettext( 474 "%s: %s: invalid file type\n"), 475 prog_name, filename); 476 errflag++; 477 } 478 } 479 (void) elf_end(elf_file); 480 (void) close(fd); 481 } 482 483 /* 484 * Get the ELF header and, if it exists, call get_symtab() 485 * to begin processing of the file; otherwise, return from 486 * processing the file with a warning. 487 */ 488 static void 489 process(Elf *elf_file, char *filename) 490 { 491 GElf_Ehdr ehdr; 492 493 if (gelf_getehdr(elf_file, &ehdr) == NULL) { 494 (void) fprintf(stderr, 495 "%s: %s: %s\n", prog_name, filename, elf_errmsg(-1)); 496 return; 497 } 498 499 set_A_header(filename); 500 get_symtab(elf_file, filename); 501 } 502 503 /* 504 * Get section descriptor for the associated string table 505 * and verify that the type of the section pointed to is 506 * indeed of type STRTAB. Returns a valid section descriptor 507 * or NULL on error. 508 */ 509 static Elf_Scn * 510 get_scnfd(Elf * e_file, int shstrtab, int SCN_TYPE) 511 { 512 Elf_Scn *fd_scn; 513 GElf_Shdr shdr; 514 515 if ((fd_scn = elf_getscn(e_file, shstrtab)) == NULL) { 516 return (NULL); 517 } 518 519 (void) gelf_getshdr(fd_scn, &shdr); 520 if (shdr.sh_type != SCN_TYPE) { 521 return (NULL); 522 } 523 return (fd_scn); 524 } 525 526 527 /* 528 * Print the symbol table. This function does not print the contents 529 * of the symbol table but sets up the parameters and then calls 530 * print_symtab to print the symbols. This function does not assume 531 * that there is only one section of type SYMTAB. Input is an opened 532 * ELF file, a pointer to the ELF header, and the filename. 533 */ 534 static void 535 get_symtab(Elf *elf_file, char *filename) 536 { 537 Elf_Scn *scn, *scnfd; 538 Elf_Data *data; 539 GElf_Word symtabtype; 540 size_t shstrndx; 541 542 if (elf_getshdrstrndx(elf_file, &shstrndx) == -1) { 543 (void) fprintf(stderr, gettext( 544 "%s: %s: cannot get e_shstrndx\n"), 545 prog_name, filename); 546 return; 547 } 548 549 /* get section header string table */ 550 scnfd = get_scnfd(elf_file, shstrndx, SHT_STRTAB); 551 if (scnfd == NULL) { 552 (void) fprintf(stderr, gettext( 553 "%s: %s: cannot get string table\n"), 554 prog_name, filename); 555 return; 556 } 557 558 data = elf_getdata(scnfd, NULL); 559 if (data->d_size == 0) { 560 (void) fprintf(stderr, gettext( 561 "%s: %s: no data in string table\n"), 562 prog_name, filename); 563 return; 564 } 565 566 if (D_flag) 567 symtabtype = SHT_DYNSYM; 568 else if (L_flag) 569 symtabtype = SHT_SUNW_LDYNSYM; 570 else 571 symtabtype = SHT_SYMTAB; 572 573 scn = 0; 574 while ((scn = elf_nextscn(elf_file, scn)) != 0) { 575 GElf_Shdr shdr; 576 577 if (gelf_getshdr(scn, &shdr) == NULL) { 578 (void) fprintf(stderr, "%s: %s: %s:\n", 579 prog_name, filename, elf_errmsg(-1)); 580 return; 581 } 582 583 if (shdr.sh_type == symtabtype) { 584 print_symtab(elf_file, shstrndx, scn, 585 &shdr, filename); 586 } 587 } /* end while */ 588 } 589 590 /* 591 * Process member files of an archive. This function provides 592 * a loop through an archive equivalent the processing of 593 * each_file for individual object files. 594 */ 595 static void 596 print_ar_files(int fd, Elf * elf_file, char *filename) 597 { 598 Elf_Arhdr *p_ar; 599 Elf *arf; 600 Elf_Cmd cmd; 601 Elf_Kind file_type; 602 603 604 cmd = ELF_C_READ; 605 archive_name = filename; 606 while ((arf = elf_begin(fd, cmd, elf_file)) != 0) { 607 p_ar = elf_getarhdr(arf); 608 if (p_ar == NULL) { 609 (void) fprintf(stderr, "%s: %s: %s\n", 610 prog_name, filename, elf_errmsg(-1)); 611 return; 612 } 613 if (p_ar->ar_name[0] == '/') { 614 cmd = elf_next(arf); 615 (void) elf_end(arf); 616 continue; 617 } 618 619 if (!h_flag & !P_flag) { 620 if (p_flag) 621 (void) printf("\n\n%s[%s]:\n", 622 filename, p_ar->ar_name); 623 else { 624 if (A_flag != 0) 625 (void) printf("\n\n%s%s[%s]:\n", 626 A_header, filename, p_ar->ar_name); 627 else 628 (void) printf("\n\n%s[%s]:\n", 629 filename, p_ar->ar_name); 630 } 631 } 632 file_type = elf_kind(arf); 633 if (file_type == ELF_K_ELF) { 634 process(arf, p_ar->ar_name); 635 } else { 636 (void) fprintf(stderr, gettext( 637 "%s: %s: invalid file type\n"), 638 prog_name, p_ar->ar_name); 639 cmd = elf_next(arf); 640 (void) elf_end(arf); 641 errflag++; 642 continue; 643 } 644 645 cmd = elf_next(arf); 646 (void) elf_end(arf); 647 } /* end while */ 648 } 649 650 static void print_header(int); 651 #ifndef XPG4 652 static void print_with_uflag(SYM *, char *); 653 #endif 654 static void print_with_pflag(int, Elf *, unsigned int, SYM *, char *); 655 static void print_with_Pflag(int, Elf *, unsigned int, SYM *); 656 static void print_with_otherflags(int, Elf *, unsigned int, 657 SYM *, char *); 658 /* 659 * Print the symbol table according to the flags that were 660 * set, if any. Input is an opened ELF file, the section name, 661 * the section header, the section descriptor, and the filename. 662 * First get the symbol table with a call to elf_getdata. 663 * Then translate the symbol table data in memory by calling 664 * readsyms(). This avoids duplication of function calls 665 * and improves sorting efficiency. qsort is used when sorting 666 * is requested. 667 */ 668 static void 669 print_symtab(Elf *elf_file, unsigned int shstrndx, 670 Elf_Scn *p_sd, GElf_Shdr *shdr, char *filename) 671 { 672 673 Elf_Data * sd; 674 SYM *sym_data; 675 SYM *s; 676 GElf_Sxword count = 0; 677 const int ndigits_arr[] = { 678 10, /* FMT_T_DEC */ 679 8, /* FMT_T_HEX */ 680 11, /* FMT_T_OCT */ 681 }; 682 int ndigits; 683 684 /* 685 * Determine # of digits to use for each numeric value. 686 */ 687 ndigits = ndigits_arr[fmt_flag]; 688 if (gelf_getclass(elf_file) == ELFCLASS64) 689 ndigits *= 2; 690 691 /* 692 * print header 693 */ 694 print_header(ndigits); 695 696 /* 697 * get symbol table data 698 */ 699 if (((sd = elf_getdata(p_sd, NULL)) == NULL) || (sd->d_size == 0)) { 700 (void) fprintf(stderr, 701 gettext("%s: %s: no symbol table data\n"), 702 prog_name, filename); 703 return; 704 } 705 count = shdr->sh_size / shdr->sh_entsize; 706 707 /* 708 * translate symbol table data 709 */ 710 sym_data = readsyms(sd, count, elf_file, shdr->sh_link, 711 (unsigned int)elf_ndxscn(p_sd)); 712 if (sym_data == NULL) { 713 (void) fprintf(stderr, gettext( 714 "%s: %s: problem reading symbol data\n"), 715 prog_name, filename); 716 return; 717 } 718 qsort((char *)sym_data, count-1, sizeof (SYM), 719 (int (*)(const void *, const void *))compare); 720 s = sym_data; 721 while (count > 1) { 722 #ifndef XPG4 723 if (u_flag) { 724 /* 725 * U_flag specified 726 */ 727 print_with_uflag(sym_data, filename); 728 } else if (p_flag) 729 #else 730 if (p_flag) 731 #endif 732 print_with_pflag(ndigits, elf_file, shstrndx, 733 sym_data, filename); 734 else if (P_flag) 735 print_with_Pflag(ndigits, elf_file, shstrndx, 736 sym_data); 737 else 738 print_with_otherflags(ndigits, elf_file, 739 shstrndx, sym_data, filename); 740 sym_data++; 741 count--; 742 } 743 744 free(s); /* allocated in readsym() */ 745 } 746 747 /* 748 * Return appropriate keyletter(s) for -p option. 749 * Returns an index into the key[][] table or NULL if 750 * the value of the keyletter is unknown. 751 */ 752 static char * 753 lookup(int a, int b) 754 { 755 return (((a < TYPE) && (b < BIND)) ? key[a][b] : NULL); 756 } 757 758 /* 759 * Return TRUE(1) if the given section is ".bss" for "-p" option. 760 * Return FALSE(0) if not ".bss" section. 761 */ 762 static int 763 is_bss_section(unsigned int shndx, Elf * elf_file, unsigned int shstrndx) 764 { 765 Elf_Scn *scn = elf_getscn(elf_file, shndx); 766 char *sym_name; 767 768 if (scn != NULL) { 769 GElf_Shdr shdr; 770 (void) gelf_getshdr(scn, &shdr); 771 sym_name = elf_strptr(elf_file, shstrndx, shdr.sh_name); 772 if (strcmp(BSS_SECN, sym_name) == 0) 773 return (1); 774 } 775 return (0); 776 } 777 778 /* 779 * Translate symbol table data particularly for sorting. 780 * Input is the symbol table data structure, number of symbols, 781 * opened ELF file, and the string table link offset. 782 */ 783 static SYM * 784 readsyms(Elf_Data * data, GElf_Sxword num, Elf *elf, 785 unsigned int link, unsigned int symscnndx) 786 { 787 SYM *s, *buf; 788 GElf_Sym sym; 789 Elf32_Word *symshndx = 0; 790 unsigned int nosymshndx = 0; 791 int i; 792 793 if ((buf = calloc(num, sizeof (SYM))) == NULL) { 794 (void) fprintf(stderr, gettext("%s: cannot allocate memory\n"), 795 prog_name); 796 return (NULL); 797 } 798 799 s = buf; /* save pointer to head of array */ 800 801 for (i = 1; i < num; i++, buf++) { 802 (void) gelf_getsym(data, i, &sym); 803 804 buf->indx = i; 805 /* allow to work on machines where NULL-derefs dump core */ 806 if (sym.st_name == 0) 807 buf->name = ""; 808 else if (C_flag) { 809 const char *dn = NULL; 810 char *name = (char *)elf_strptr(elf, link, sym.st_name); 811 812 dn = conv_demangle_name(name); 813 if (dn != name) { 814 name = FormatName(name, dn); 815 free((void *)dn); 816 } else if (exotic(name)) { 817 name = FormatName(name, d_buf); 818 } 819 buf->name = name; 820 } 821 else 822 buf->name = (char *)elf_strptr(elf, link, sym.st_name); 823 824 buf->value = sym.st_value; 825 buf->size = sym.st_size; 826 buf->type = GELF_ST_TYPE(sym.st_info); 827 buf->bind = GELF_ST_BIND(sym.st_info); 828 buf->other = sym.st_other; 829 if ((sym.st_shndx == SHN_XINDEX) && 830 (symshndx == 0) && (nosymshndx == 0)) { 831 Elf_Scn *_scn; 832 GElf_Shdr _shdr; 833 _scn = 0; 834 while ((_scn = elf_nextscn(elf, _scn)) != 0) { 835 if (gelf_getshdr(_scn, &_shdr) == 0) 836 break; 837 if ((_shdr.sh_type == SHT_SYMTAB_SHNDX) && 838 (_shdr.sh_link == symscnndx)) { 839 Elf_Data *_data; 840 if ((_data = elf_getdata(_scn, 841 0)) != 0) { 842 symshndx = 843 (Elf32_Word *)_data->d_buf; 844 break; 845 } 846 } 847 } 848 nosymshndx = 1; 849 } 850 if ((symshndx) && (sym.st_shndx == SHN_XINDEX)) { 851 buf->shndx = symshndx[i]; 852 } else { 853 buf->shndx = sym.st_shndx; 854 if (sym.st_shndx >= SHN_LORESERVE) 855 buf->flags |= FLG_SYM_SPECSEC; 856 } 857 } /* end for loop */ 858 return (s); 859 } 860 861 /* 862 * compare either by name or by value for sorting. 863 * This is the comparison function called by qsort to 864 * sort the symbols either by name or value when requested. 865 */ 866 static int 867 compare(SYM *a, SYM *b) 868 { 869 if (v_flag) { 870 if (a->value > b->value) 871 return (1); 872 else 873 return ((a->value == b->value) -1); 874 } else 875 return ((int)strcoll(a->name, b->name)); 876 } 877 878 /* 879 * Set up a header line for -A option. 880 */ 881 static void 882 set_A_header(char *fname) 883 { 884 if (A_flag == 0) 885 return; 886 887 if (archive_name == (char *)0) { 888 (void) snprintf(A_header, sizeof (A_header), "%s: ", fname); 889 } else { 890 (void) snprintf(A_header, sizeof (A_header), "%s[%s]: ", 891 archive_name, fname); 892 } 893 } 894 895 /* 896 * output functions 897 * The following functions are called from 898 * print_symtab(). 899 */ 900 901 /* 902 * Print header line if needed. 903 * 904 * entry: 905 * ndigits - # of digits to be used to format an integer 906 * value, not counting any '0x' (hex) or '0' (octal) prefix. 907 */ 908 static void 909 print_header(int ndigits) 910 { 911 const char *fmt; 912 const char *section_title; 913 const int pad[] = { /* Extra prefix characters for format */ 914 1, /* FMT_T_DEC: '|' */ 915 3, /* FMT_T_HEX: '|0x' */ 916 2, /* FMT_T_OCT: '|0' */ 917 }; 918 if ( 919 #ifndef XPG4 920 !u_flag && 921 #endif 922 !h_flag && !p_flag && !P_flag) { 923 (void) printf("\n"); 924 if (!s_flag) { 925 fmt = "%-9s%-*s%-*s%-6s%-6s%-6s%-8s%s\n\n"; 926 section_title = "Shndx"; 927 } else { 928 fmt = "%-9s%-*s%-*s%-6s%-6s%-6s%-15s%s\n\n"; 929 section_title = "Shname"; 930 } 931 if (A_flag != 0) 932 (void) printf("%s", A_header); 933 ndigits += pad[fmt_flag]; 934 (void) printf(fmt, "[Index]", ndigits, " Value", 935 ndigits, " Size", "Type", "Bind", 936 "Other", section_title, "Name"); 937 } 938 } 939 940 /* 941 * If the symbol can be printed, then return 1. 942 * If the symbol can not be printed, then return 0. 943 */ 944 static int 945 is_sym_print(SYM *sym_data) 946 { 947 /* 948 * If -u flag is specified, 949 * the symbol has to be undefined. 950 */ 951 if (u_flag != 0) { 952 if ((sym_data->shndx == SHN_UNDEF) && 953 (strlen(sym_data->name) != 0)) 954 return (1); 955 else 956 return (0); 957 } 958 959 /* 960 * If -e flag is specified, 961 * the symbol has to be global or static. 962 */ 963 if (e_flag != 0) { 964 switch (sym_data->type) { 965 case STT_NOTYPE: 966 case STT_OBJECT: 967 case STT_FUNC: 968 case STT_COMMON: 969 case STT_TLS: 970 switch (sym_data->bind) { 971 case STB_LOCAL: 972 case STB_GLOBAL: 973 case STB_WEAK: 974 return (1); 975 default: 976 return (0); 977 } 978 default: 979 return (0); 980 } 981 } 982 983 /* 984 * If -g is specified, 985 * the symbol has to be global. 986 */ 987 if (g_flag != 0) { 988 switch (sym_data->type) { 989 case STT_NOTYPE: 990 case STT_OBJECT: 991 case STT_FUNC: 992 case STT_COMMON: 993 case STT_TLS: 994 switch (sym_data->bind) { 995 case STB_GLOBAL: 996 case STB_WEAK: 997 return (1); 998 default: 999 return (0); 1000 } 1001 default: 1002 return (0); 1003 } 1004 } 1005 1006 /* 1007 * If it comes here, any symbol can be printed. 1008 * (So basically, -f is no-op.) 1009 */ 1010 return (1); 1011 } 1012 1013 #ifndef XPG4 1014 /* 1015 * -u flag specified 1016 */ 1017 static void 1018 print_with_uflag( 1019 SYM *sym_data, 1020 char *filename 1021 ) 1022 { 1023 if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name))) { 1024 if (!r_flag) { 1025 if (R_flag) { 1026 if (archive_name != (char *)0) 1027 (void) printf(" %s:%s:%s\n", 1028 archive_name, filename, 1029 sym_data->name); 1030 else 1031 (void) printf(" %s:%s\n", 1032 filename, sym_data->name); 1033 } 1034 else 1035 (void) printf(" %s\n", sym_data->name); 1036 } 1037 else 1038 (void) printf(" %s:%s\n", filename, sym_data->name); 1039 } 1040 } 1041 #endif 1042 1043 /* 1044 * Print a symbol type representation suitable for the -p or -P formats. 1045 */ 1046 static void 1047 print_brief_sym_type(Elf *elf_file, unsigned int shstrndx, SYM *sym_data) 1048 { 1049 const char *sym_key = NULL; 1050 1051 if ((sym_data->shndx == SHN_UNDEF) && (strlen(sym_data->name))) 1052 sym_key = UNDEFINED; 1053 else if (sym_data->type == STT_SPARC_REGISTER) { 1054 switch (sym_data->bind) { 1055 case STB_LOCAL : sym_key = REG_LOCL; 1056 break; 1057 case STB_GLOBAL : sym_key = REG_GLOB; 1058 break; 1059 case STB_WEAK : sym_key = REG_WEAK; 1060 break; 1061 default : sym_key = REG_GLOB; 1062 break; 1063 } 1064 } else if (((sym_data->flags & FLG_SYM_SPECSEC) == 0) && 1065 is_bss_section((int)sym_data->shndx, elf_file, shstrndx)) { 1066 switch (sym_data->bind) { 1067 case STB_LOCAL : sym_key = BSS_LOCL; 1068 break; 1069 case STB_GLOBAL : sym_key = BSS_GLOB; 1070 break; 1071 case STB_WEAK : sym_key = BSS_WEAK; 1072 break; 1073 default : sym_key = BSS_GLOB; 1074 break; 1075 } 1076 1077 } else { 1078 sym_key = lookup(sym_data->type, sym_data->bind); 1079 } 1080 1081 if (sym_key != NULL) { 1082 if (!l_flag) 1083 (void) printf("%c ", sym_key[0]); 1084 else 1085 (void) printf("%-3s", sym_key); 1086 } else { 1087 if (!l_flag) 1088 (void) printf("%-2d", sym_data->type); 1089 else 1090 (void) printf("%-3d", sym_data->type); 1091 } 1092 } 1093 1094 /* 1095 * -p flag specified 1096 */ 1097 static void 1098 print_with_pflag( 1099 int ndigits, 1100 Elf *elf_file, 1101 unsigned int shstrndx, 1102 SYM *sym_data, 1103 char *filename 1104 ) 1105 { 1106 const char * const fmt[] = { 1107 "%.*llu ", /* FMT_T_DEC */ 1108 "0x%.*llx ", /* FMT_T_HEX */ 1109 "0%.*llo " /* FMT_T_OCT */ 1110 }; 1111 1112 if (is_sym_print(sym_data) != 1) 1113 return; 1114 /* 1115 * -A header 1116 */ 1117 if (A_flag != 0) 1118 (void) printf("%s", A_header); 1119 1120 /* 1121 * Symbol Value. 1122 * (hex/octal/decimal) 1123 */ 1124 (void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value)); 1125 1126 1127 /* 1128 * Symbol Type. 1129 */ 1130 print_brief_sym_type(elf_file, shstrndx, sym_data); 1131 1132 if (!r_flag) { 1133 if (R_flag) { 1134 if (archive_name != (char *)0) 1135 (void) printf("%s:%s:%s\n", archive_name, 1136 filename, sym_data->name); 1137 else 1138 (void) printf("%s:%s\n", filename, 1139 sym_data->name); 1140 } 1141 else 1142 (void) printf("%s\n", sym_data->name); 1143 } 1144 else 1145 (void) printf("%s:%s\n", filename, sym_data->name); 1146 } 1147 1148 /* 1149 * -P flag specified 1150 */ 1151 static void 1152 print_with_Pflag( 1153 int ndigits, 1154 Elf *elf_file, 1155 unsigned int shstrndx, 1156 SYM *sym_data 1157 ) 1158 { 1159 #define SYM_LEN 10 1160 char sym_name[SYM_LEN+1]; 1161 size_t len; 1162 const char * const fmt[] = { 1163 "%*llu %*llu \n", /* FMT_T_DEC */ 1164 "%*llx %*llx \n", /* FMT_T_HEX */ 1165 "%*llo %*llo \n" /* FMT_T_OCT */ 1166 }; 1167 1168 if (is_sym_print(sym_data) != 1) 1169 return; 1170 /* 1171 * -A header 1172 */ 1173 if (A_flag != 0) 1174 (void) printf("%s", A_header); 1175 1176 /* 1177 * Symbol name 1178 */ 1179 len = strlen(sym_data->name); 1180 if (len >= SYM_LEN) 1181 (void) printf("%s ", sym_data->name); 1182 else { 1183 (void) sprintf(sym_name, "%-10s", sym_data->name); 1184 (void) printf("%s ", sym_name); 1185 } 1186 1187 /* 1188 * Symbol Type. 1189 */ 1190 print_brief_sym_type(elf_file, shstrndx, sym_data); 1191 1192 /* 1193 * Symbol Value & size 1194 * (hex/octal/decimal) 1195 */ 1196 (void) printf(fmt[fmt_flag], ndigits, EC_ADDR(sym_data->value), 1197 ndigits, EC_XWORD(sym_data->size)); 1198 } 1199 1200 /* 1201 * other flags specified 1202 */ 1203 static void 1204 print_with_otherflags( 1205 int ndigits, 1206 Elf *elf_file, 1207 unsigned int shstrndx, 1208 SYM *sym_data, 1209 char *filename 1210 ) 1211 { 1212 const char * const fmt_value_size[] = { 1213 "%*llu|%*lld|", /* FMT_T_DEC */ 1214 "0x%.*llx|0x%.*llx|", /* FMT_T_HEX */ 1215 "0%.*llo|0%.*llo|" /* FMT_T_OCT */ 1216 }; 1217 const char * const fmt_int[] = { 1218 "%-5d", /* FMT_T_DEC */ 1219 "%#-5x", /* FMT_T_HEX */ 1220 "%#-5o" /* FMT_T_OCT */ 1221 }; 1222 1223 if (is_sym_print(sym_data) != 1) 1224 return; 1225 (void) printf("%s", A_header); 1226 (void) printf("[%d]\t|", sym_data->indx); 1227 (void) printf(fmt_value_size[fmt_flag], ndigits, 1228 EC_ADDR(sym_data->value), ndigits, EC_XWORD(sym_data->size)); 1229 1230 switch (sym_data->type) { 1231 case STT_NOTYPE:(void) printf("%-5s", "NOTY"); break; 1232 case STT_OBJECT:(void) printf("%-5s", "OBJT"); break; 1233 case STT_FUNC: (void) printf("%-5s", "FUNC"); break; 1234 case STT_SECTION:(void) printf("%-5s", "SECT"); break; 1235 case STT_FILE: (void) printf("%-5s", "FILE"); break; 1236 case STT_COMMON: (void) printf("%-5s", "COMM"); break; 1237 case STT_TLS: (void) printf("%-5s", "TLS "); break; 1238 case STT_SPARC_REGISTER: (void) printf("%-5s", "REGI"); break; 1239 default: 1240 (void) printf(fmt_int[fmt_flag], sym_data->type); 1241 } 1242 (void) printf("|"); 1243 switch (sym_data->bind) { 1244 case STB_LOCAL: (void) printf("%-5s", "LOCL"); break; 1245 case STB_GLOBAL:(void) printf("%-5s", "GLOB"); break; 1246 case STB_WEAK: (void) printf("%-5s", "WEAK"); break; 1247 default: 1248 (void) printf("%-5d", sym_data->bind); 1249 (void) printf(fmt_int[fmt_flag], sym_data->bind); 1250 } 1251 (void) printf("|"); 1252 (void) printf(fmt_int[fmt_flag], sym_data->other); 1253 (void) printf("|"); 1254 1255 if (sym_data->shndx == SHN_UNDEF) { 1256 if (!s_flag) 1257 (void) printf("%-7s", "UNDEF"); 1258 else 1259 (void) printf("%-14s", "UNDEF"); 1260 } else if (sym_data->shndx == SHN_SUNW_IGNORE) { 1261 if (!s_flag) 1262 (void) printf("%-7s", "IGNORE"); 1263 else 1264 (void) printf("%-14s", "IGNORE"); 1265 } else if ((sym_data->flags & FLG_SYM_SPECSEC) && 1266 (sym_data->shndx == SHN_ABS)) { 1267 if (!s_flag) 1268 (void) printf("%-7s", "ABS"); 1269 else 1270 (void) printf("%-14s", "ABS"); 1271 } else if ((sym_data->flags & FLG_SYM_SPECSEC) && 1272 (sym_data->shndx == SHN_COMMON)) { 1273 if (!s_flag) 1274 (void) printf("%-7s", "COMMON"); 1275 else 1276 (void) printf("%-14s", "COMMON"); 1277 } else { 1278 if (s_flag) { 1279 Elf_Scn *scn = elf_getscn(elf_file, sym_data->shndx); 1280 GElf_Shdr shdr; 1281 1282 if ((gelf_getshdr(scn, &shdr) != 0) && 1283 (shdr.sh_name != 0)) { 1284 (void) printf("%-14s", 1285 (char *)elf_strptr(elf_file, 1286 shstrndx, shdr.sh_name)); 1287 } else { 1288 (void) printf("%-14d", sym_data->shndx); 1289 } 1290 } else { 1291 (void) printf("%-7d", sym_data->shndx); 1292 } 1293 } 1294 (void) printf("|"); 1295 if (!r_flag) { 1296 if (R_flag) { 1297 if (archive_name != (char *)0) 1298 (void) printf("%s:%s:%s\n", archive_name, 1299 filename, sym_data->name); 1300 else 1301 (void) printf("%s:%s\n", filename, 1302 sym_data->name); 1303 } 1304 else 1305 (void) printf("%s\n", sym_data->name); 1306 } 1307 else 1308 (void) printf("%s:%s\n", filename, sym_data->name); 1309 } 1310 1311 /* 1312 * C++ name demangling supporting routines 1313 */ 1314 static const char *ctor_str = "static constructor function for %s"; 1315 static const char *dtor_str = "static destructor function for %s"; 1316 static const char *ptbl_str = "pointer to the virtual table vector for %s"; 1317 static const char *vtbl_str = "virtual table for %s"; 1318 1319 /* 1320 * alloc memory and create name in necessary format. 1321 * Return name string 1322 */ 1323 static char * 1324 FormatName(char *OldName, const char *NewName) 1325 { 1326 char *s = p_flag ? 1327 "%s\n [%s]" : 1328 "%s\n\t\t\t\t\t\t [%s]"; 1329 size_t length = strlen(s)+strlen(NewName)+strlen(OldName)-3; 1330 char *hold = OldName; 1331 OldName = malloc(length); 1332 /*LINTED*/ 1333 (void) snprintf(OldName, length, s, NewName, hold); 1334 return (OldName); 1335 } 1336 1337 1338 /* 1339 * Return 1 when s is an exotic name, 0 otherwise. s remains unchanged, 1340 * the exotic name, if exists, is saved in d_buf. 1341 */ 1342 static int 1343 exotic(const char *in_str) 1344 { 1345 static char *buff = 0; 1346 static size_t buf_size; 1347 1348 size_t sym_len = strlen(in_str) + 1; 1349 int tag = 0; 1350 char *s; 1351 1352 /* 1353 * We will need to modify the symbol (in_str) as we are analyzing it, 1354 * so copy it into a buffer so that we can play around with it. 1355 */ 1356 if (buff == NULL) { 1357 buff = malloc(DEF_MAX_SYM_SIZE); 1358 buf_size = DEF_MAX_SYM_SIZE; 1359 } 1360 1361 if (sym_len > buf_size) { 1362 if (buff) 1363 free(buff); 1364 buff = malloc(sym_len); 1365 buf_size = sym_len; 1366 } 1367 1368 if (buff == NULL) { 1369 (void) fprintf(stderr, gettext( 1370 "%s: cannot allocate memory\n"), prog_name); 1371 exit(NOALLOC); 1372 } 1373 s = strcpy(buff, in_str); 1374 1375 1376 if (strncmp(s, "__sti__", 7) == 0) { 1377 s += 7; tag = 1; 1378 parse_fn_and_print(ctor_str, s); 1379 } else if (strncmp(s, "__std__", 7) == 0) { 1380 s += 7; tag = 1; 1381 parse_fn_and_print(dtor_str, s); 1382 } else if (strncmp(s, "__vtbl__", 8) == 0) { 1383 s += 8; tag = 1; 1384 parsename(s); 1385 (void) sprintf(d_buf, vtbl_str, p_buf); 1386 } else if (strncmp(s, "__ptbl_vec__", 12) == 0) { 1387 s += 12; tag = 1; 1388 parse_fn_and_print(ptbl_str, s); 1389 } 1390 return (tag); 1391 } 1392 1393 void 1394 parsename(char *s) 1395 { 1396 register int len; 1397 char c, *orig = s; 1398 *p_buf = '\0'; 1399 (void) strcat(p_buf, "class "); 1400 while (isdigit(*s)) s++; 1401 c = *s; 1402 *s = '\0'; 1403 len = atoi(orig); 1404 *s = c; 1405 if (*(s+len) == '\0') { /* only one class name */ 1406 (void) strcat(p_buf, s); 1407 return; 1408 } else 1409 { /* two classname %drootname__%dchildname */ 1410 char *root, *child, *child_len_p; 1411 int child_len; 1412 root = s; 1413 child = s + len + 2; 1414 child_len_p = child; 1415 if (!isdigit(*child)) { 1416 /* ptbl file name */ 1417 /* %drootname__%filename */ 1418 /* kludge for getting rid of '_' in file name */ 1419 char *p; 1420 c = *(root + len); 1421 *(root + len) = '\0'; 1422 (void) strcat(p_buf, root); 1423 *(root + len) = c; 1424 (void) strcat(p_buf, " in "); 1425 for (p = child; *p != '_'; ++p) 1426 ; 1427 c = *p; 1428 *p = '.'; 1429 (void) strcat(p_buf, child); 1430 *p = c; 1431 return; 1432 } 1433 1434 while (isdigit(*child)) 1435 child++; 1436 c = *child; 1437 *child = '\0'; 1438 child_len = atoi(child_len_p); 1439 *child = c; 1440 if (*(child + child_len) == '\0') { 1441 (void) strcat(p_buf, child); 1442 (void) strcat(p_buf, " derived from "); 1443 c = *(root + len); 1444 *(root + len) = '\0'; 1445 (void) strcat(p_buf, root); 1446 *(root + len) = c; 1447 return; 1448 } else { 1449 /* %drootname__%dchildname__filename */ 1450 /* kludge for getting rid of '_' in file name */ 1451 char *p; 1452 c = *(child + child_len); 1453 *(child + child_len) = '\0'; 1454 (void) strcat(p_buf, child); 1455 *(child+child_len) = c; 1456 (void) strcat(p_buf, " derived from "); 1457 c = *(root + len); 1458 *(root + len) = '\0'; 1459 (void) strcat(p_buf, root); 1460 *(root + len) = c; 1461 (void) strcat(p_buf, " in "); 1462 for (p = child + child_len + 2; *p != '_'; ++p) 1463 ; 1464 c = *p; 1465 *p = '.'; 1466 (void) strcat(p_buf, child + child_len + 2); 1467 *p = c; 1468 return; 1469 } 1470 } 1471 } 1472 1473 void 1474 parse_fn_and_print(const char *str, char *s) 1475 { 1476 char c, *p1, *p2; 1477 int yes = 1; 1478 1479 if ((p1 = p2 = strstr(s, "_c_")) == NULL) 1480 if ((p1 = p2 = strstr(s, "_C_")) == NULL) 1481 if ((p1 = p2 = strstr(s, "_cc_")) == NULL) 1482 if ((p1 = p2 = strstr(s, "_cxx_")) == NULL) 1483 if ((p1 = p2 = strstr(s, "_h_")) == 1484 NULL) 1485 yes = 0; 1486 else 1487 p2 += 2; 1488 else 1489 p2 += 4; 1490 else 1491 p2 += 3; 1492 else 1493 p2 += 2; 1494 else 1495 p2 += 2; 1496 1497 if (yes) { 1498 *p1 = '.'; 1499 c = *p2; 1500 *p2 = '\0'; 1501 } 1502 1503 for (s = p1; *s != '_'; --s) 1504 ; 1505 ++s; 1506 1507 (void) sprintf(d_buf, str, s); 1508 1509 if (yes) { 1510 *p1 = '_'; 1511 *p2 = c; 1512 } 1513 }