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