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