17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
57a5d89c4Sab * Common Development and Distribution License (the "License").
67a5d89c4Sab * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
2192ed1782Smike_s
227c478bd9Sstevel@tonic-gate /*
237a5d89c4Sab * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
2492ed1782Smike_s * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #include "gprof.h"
287c478bd9Sstevel@tonic-gate #include <stdlib.h>
297c478bd9Sstevel@tonic-gate #include <sys/file.h>
307c478bd9Sstevel@tonic-gate #include <fcntl.h>
317c478bd9Sstevel@tonic-gate #include <unistd.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <sysexits.h>
347c478bd9Sstevel@tonic-gate #include <libelf.h>
35*326c1bafSToomas Soome #include "gelf.h"
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate #ifdef DEBUG
387c478bd9Sstevel@tonic-gate static void debug_dup_del(nltype *, nltype *);
397c478bd9Sstevel@tonic-gate
407c478bd9Sstevel@tonic-gate #define DPRINTF(msg, file) if (debug & ELFDEBUG) \
4192ed1782Smike_s (void) printf(msg, file);
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate #define PRINTF(msg) if (debug & ELFDEBUG) \
4492ed1782Smike_s (void) printf(msg);
457c478bd9Sstevel@tonic-gate
467c478bd9Sstevel@tonic-gate #define DEBUG_DUP_DEL(keeper, louser) if (debug & ELFDEBUG) \
477c478bd9Sstevel@tonic-gate debug_dup_del(keeper, louser);
487c478bd9Sstevel@tonic-gate
497c478bd9Sstevel@tonic-gate #else
507c478bd9Sstevel@tonic-gate #define DPRINTF(msg, file)
517c478bd9Sstevel@tonic-gate #define PRINTF(msg)
527c478bd9Sstevel@tonic-gate #define DEBUG_DUP_DEL(keeper, louser)
537c478bd9Sstevel@tonic-gate #endif
547c478bd9Sstevel@tonic-gate
557c478bd9Sstevel@tonic-gate size_t textbegin, textsize;
567c478bd9Sstevel@tonic-gate
577c478bd9Sstevel@tonic-gate /* Prototype definitions first */
587c478bd9Sstevel@tonic-gate
597c478bd9Sstevel@tonic-gate static void process(char *filename, int fd);
6092ed1782Smike_s static void get_symtab(Elf *elf, mod_info_t *module);
6192ed1782Smike_s static void get_textseg(Elf *elf, int fd);
627c478bd9Sstevel@tonic-gate static void save_aout_info(char *);
637c478bd9Sstevel@tonic-gate
647c478bd9Sstevel@tonic-gate static void
fatal_error(char * error)657c478bd9Sstevel@tonic-gate fatal_error(char *error)
667c478bd9Sstevel@tonic-gate {
6792ed1782Smike_s (void) fprintf(stderr,
6892ed1782Smike_s "Fatal ELF error: %s (%s)\n", error, elf_errmsg(-1));
697c478bd9Sstevel@tonic-gate exit(EX_SOFTWARE);
707c478bd9Sstevel@tonic-gate }
717c478bd9Sstevel@tonic-gate
727c478bd9Sstevel@tonic-gate bool
is_shared_obj(char * name)737c478bd9Sstevel@tonic-gate is_shared_obj(char *name)
747c478bd9Sstevel@tonic-gate {
757c478bd9Sstevel@tonic-gate int fd;
767c478bd9Sstevel@tonic-gate Elf *elf;
777c478bd9Sstevel@tonic-gate GElf_Ehdr ehdr;
787c478bd9Sstevel@tonic-gate
797c478bd9Sstevel@tonic-gate if ((fd = open(name, O_RDONLY)) == -1) {
8092ed1782Smike_s (void) fprintf(stderr, "%s: can't open `%s'\n", whoami, name);
817c478bd9Sstevel@tonic-gate exit(EX_NOINPUT);
827c478bd9Sstevel@tonic-gate }
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate if (elf_version(EV_CURRENT) == EV_NONE)
857c478bd9Sstevel@tonic-gate fatal_error("libelf is out of date");
867c478bd9Sstevel@tonic-gate
877c478bd9Sstevel@tonic-gate if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
887c478bd9Sstevel@tonic-gate fatal_error("can't read as ELF file");
897c478bd9Sstevel@tonic-gate
907c478bd9Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL)
917c478bd9Sstevel@tonic-gate fatal_error("can't read ehdr");
927c478bd9Sstevel@tonic-gate
9392ed1782Smike_s (void) elf_end(elf);
9492ed1782Smike_s (void) close(fd);
957c478bd9Sstevel@tonic-gate
967c478bd9Sstevel@tonic-gate if (ehdr.e_type == ET_DYN)
977c478bd9Sstevel@tonic-gate return (TRUE);
987c478bd9Sstevel@tonic-gate else
997c478bd9Sstevel@tonic-gate return (FALSE);
1007c478bd9Sstevel@tonic-gate }
1017c478bd9Sstevel@tonic-gate
1027c478bd9Sstevel@tonic-gate static void
save_aout_info(char * aoutname)1037c478bd9Sstevel@tonic-gate save_aout_info(char *aoutname)
1047c478bd9Sstevel@tonic-gate {
1057c478bd9Sstevel@tonic-gate struct stat buf;
1067c478bd9Sstevel@tonic-gate extern fl_info_t aout_info;
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate if (stat(aoutname, &buf) == -1) {
10992ed1782Smike_s (void) fprintf(stderr, "%s: can't get info on `%s'\n",
110*326c1bafSToomas Soome whoami, aoutname);
1117c478bd9Sstevel@tonic-gate exit(EX_NOINPUT);
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate
1147c478bd9Sstevel@tonic-gate aout_info.dev = buf.st_dev;
1157c478bd9Sstevel@tonic-gate aout_info.ino = buf.st_ino;
1167c478bd9Sstevel@tonic-gate aout_info.mtime = buf.st_mtime;
1177c478bd9Sstevel@tonic-gate aout_info.size = buf.st_size;
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate
1207c478bd9Sstevel@tonic-gate void
getnfile(char * aoutname)1217c478bd9Sstevel@tonic-gate getnfile(char *aoutname)
1227c478bd9Sstevel@tonic-gate {
1237c478bd9Sstevel@tonic-gate int fd;
1247c478bd9Sstevel@tonic-gate
1257c478bd9Sstevel@tonic-gate DPRINTF(" Attempting to open %s \n", aoutname);
1267c478bd9Sstevel@tonic-gate if ((fd = open((aoutname), O_RDONLY)) == -1) {
1277c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: can't open `%s'\n",
128*326c1bafSToomas Soome whoami, aoutname);
1297c478bd9Sstevel@tonic-gate exit(EX_NOINPUT);
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate process(aoutname, fd);
1327c478bd9Sstevel@tonic-gate save_aout_info(aoutname);
1337c478bd9Sstevel@tonic-gate
1347c478bd9Sstevel@tonic-gate (void) close(fd);
1357c478bd9Sstevel@tonic-gate }
1367c478bd9Sstevel@tonic-gate
1377c478bd9Sstevel@tonic-gate static GElf_Addr
get_txtorigin(Elf * elf)1387c478bd9Sstevel@tonic-gate get_txtorigin(Elf *elf)
1397c478bd9Sstevel@tonic-gate {
1407c478bd9Sstevel@tonic-gate GElf_Ehdr ehdr;
1417c478bd9Sstevel@tonic-gate GElf_Phdr phdr;
1427c478bd9Sstevel@tonic-gate GElf_Half ndx;
1437c478bd9Sstevel@tonic-gate GElf_Addr txt_origin = 0;
1447c478bd9Sstevel@tonic-gate bool first_load_seg = TRUE;
1457c478bd9Sstevel@tonic-gate
1467c478bd9Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL)
1477c478bd9Sstevel@tonic-gate fatal_error("can't read ehdr");
1487c478bd9Sstevel@tonic-gate
1497c478bd9Sstevel@tonic-gate for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
1507c478bd9Sstevel@tonic-gate if (gelf_getphdr(elf, ndx, &phdr) == NULL)
1517c478bd9Sstevel@tonic-gate continue;
1527c478bd9Sstevel@tonic-gate
1537c478bd9Sstevel@tonic-gate if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
1547c478bd9Sstevel@tonic-gate if (first_load_seg || phdr.p_vaddr < txt_origin)
1557c478bd9Sstevel@tonic-gate txt_origin = phdr.p_vaddr;
1567c478bd9Sstevel@tonic-gate
1577c478bd9Sstevel@tonic-gate if (first_load_seg)
1587c478bd9Sstevel@tonic-gate first_load_seg = FALSE;
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate
1627c478bd9Sstevel@tonic-gate return (txt_origin);
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate void
process_namelist(mod_info_t * module)1667c478bd9Sstevel@tonic-gate process_namelist(mod_info_t *module)
1677c478bd9Sstevel@tonic-gate {
1687c478bd9Sstevel@tonic-gate int fd;
1697c478bd9Sstevel@tonic-gate Elf *elf;
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate if ((fd = open(module->name, O_RDONLY)) == -1) {
1727c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: can't read %s\n",
173*326c1bafSToomas Soome whoami, module->name);
17492ed1782Smike_s (void) fprintf(stderr, "Exiting due to error(s)...\n");
1757c478bd9Sstevel@tonic-gate exit(EX_NOINPUT);
1767c478bd9Sstevel@tonic-gate }
1777c478bd9Sstevel@tonic-gate
1787c478bd9Sstevel@tonic-gate /*
1797c478bd9Sstevel@tonic-gate * libelf's version already verified in processing a.out,
1807c478bd9Sstevel@tonic-gate * so directly do elf_begin()
1817c478bd9Sstevel@tonic-gate */
1827c478bd9Sstevel@tonic-gate if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
1837c478bd9Sstevel@tonic-gate fatal_error("can't read as ELF file");
1847c478bd9Sstevel@tonic-gate
1857c478bd9Sstevel@tonic-gate module->next = NULL;
1867c478bd9Sstevel@tonic-gate module->txt_origin = get_txtorigin(elf);
18792ed1782Smike_s get_symtab(elf, module);
1887c478bd9Sstevel@tonic-gate module->active = TRUE;
1897c478bd9Sstevel@tonic-gate }
1907c478bd9Sstevel@tonic-gate
1917c478bd9Sstevel@tonic-gate /*
1927c478bd9Sstevel@tonic-gate * Get the ELF header and, if it exists, call get_symtab()
1937c478bd9Sstevel@tonic-gate * to begin processing of the file; otherwise, return from
1947c478bd9Sstevel@tonic-gate * processing the file with a warning.
1957c478bd9Sstevel@tonic-gate */
1967c478bd9Sstevel@tonic-gate static void
process(char * filename,int fd)1977c478bd9Sstevel@tonic-gate process(char *filename, int fd)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate Elf *elf;
2007c478bd9Sstevel@tonic-gate extern bool cflag;
2017c478bd9Sstevel@tonic-gate extern bool Bflag;
2027c478bd9Sstevel@tonic-gate
2037c478bd9Sstevel@tonic-gate if (elf_version(EV_CURRENT) == EV_NONE)
2047c478bd9Sstevel@tonic-gate fatal_error("libelf is out of date");
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
2077c478bd9Sstevel@tonic-gate fatal_error("can't read as ELF file");
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate if (gelf_getclass(elf) == ELFCLASS64)
2107c478bd9Sstevel@tonic-gate Bflag = TRUE;
2117c478bd9Sstevel@tonic-gate
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate * Initialize active modules list. Note that we set the end
2147c478bd9Sstevel@tonic-gate * address while reading the symbol table, in get_symtab
2157c478bd9Sstevel@tonic-gate */
2167c478bd9Sstevel@tonic-gate modules.id = 1;
2177c478bd9Sstevel@tonic-gate modules.next = NULL;
2187c478bd9Sstevel@tonic-gate modules.txt_origin = get_txtorigin(elf);
2197c478bd9Sstevel@tonic-gate modules.load_base = modules.txt_origin;
22092ed1782Smike_s if ((modules.name = malloc(strlen(filename) + 1)) == NULL) {
22192ed1782Smike_s (void) fprintf(stderr, "%s: can't malloc %d bytes",
222*326c1bafSToomas Soome whoami, strlen(filename) + 1);
2237c478bd9Sstevel@tonic-gate exit(EX_UNAVAILABLE);
2247c478bd9Sstevel@tonic-gate }
22592ed1782Smike_s (void) strcpy(modules.name, filename);
2267c478bd9Sstevel@tonic-gate
22792ed1782Smike_s get_symtab(elf, &modules);
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate modules.load_end = modules.data_end;
2307c478bd9Sstevel@tonic-gate modules.active = TRUE;
2317c478bd9Sstevel@tonic-gate n_modules = 1;
2327c478bd9Sstevel@tonic-gate
2337c478bd9Sstevel@tonic-gate if (cflag)
23492ed1782Smike_s get_textseg(elf, fd);
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate static void
get_textseg(Elf * elf,int fd)23892ed1782Smike_s get_textseg(Elf *elf, int fd)
2397c478bd9Sstevel@tonic-gate {
2407c478bd9Sstevel@tonic-gate GElf_Ehdr ehdr;
2417c478bd9Sstevel@tonic-gate GElf_Phdr phdr;
2427c478bd9Sstevel@tonic-gate GElf_Half i;
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL)
2457c478bd9Sstevel@tonic-gate fatal_error("can't read ehdr");
2467c478bd9Sstevel@tonic-gate
2477c478bd9Sstevel@tonic-gate for (i = 0; i < ehdr.e_phnum; i++) {
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gate if (gelf_getphdr(elf, i, &phdr) == NULL)
2507c478bd9Sstevel@tonic-gate continue;
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate if (!(phdr.p_flags & PF_W) && (phdr.p_filesz > textsize)) {
2537c478bd9Sstevel@tonic-gate size_t chk;
2547c478bd9Sstevel@tonic-gate
2557c478bd9Sstevel@tonic-gate /*
2567c478bd9Sstevel@tonic-gate * We could have multiple loadable text segments;
2577c478bd9Sstevel@tonic-gate * keep the largest we find.
2587c478bd9Sstevel@tonic-gate */
2597c478bd9Sstevel@tonic-gate if (textspace)
2607c478bd9Sstevel@tonic-gate free(textspace);
2617c478bd9Sstevel@tonic-gate
2627c478bd9Sstevel@tonic-gate /*
2637c478bd9Sstevel@tonic-gate * gprof is a 32-bit program; if this text segment
2647c478bd9Sstevel@tonic-gate * has a > 32-bit offset or length, it's too big.
2657c478bd9Sstevel@tonic-gate */
2667c478bd9Sstevel@tonic-gate chk = (size_t)phdr.p_vaddr + (size_t)phdr.p_filesz;
2677c478bd9Sstevel@tonic-gate if (phdr.p_vaddr + phdr.p_filesz != (GElf_Xword)chk)
2687c478bd9Sstevel@tonic-gate fatal_error("text segment too large for -c");
2697c478bd9Sstevel@tonic-gate
2707c478bd9Sstevel@tonic-gate textbegin = (size_t)phdr.p_vaddr;
2717c478bd9Sstevel@tonic-gate textsize = (size_t)phdr.p_filesz;
2727c478bd9Sstevel@tonic-gate
2737c478bd9Sstevel@tonic-gate textspace = malloc(textsize);
2747c478bd9Sstevel@tonic-gate
2757c478bd9Sstevel@tonic-gate if (lseek(fd, (off_t)phdr.p_offset, SEEK_SET) !=
2767c478bd9Sstevel@tonic-gate (off_t)phdr.p_offset)
2777c478bd9Sstevel@tonic-gate fatal_error("cannot seek to text section");
2787c478bd9Sstevel@tonic-gate
2797c478bd9Sstevel@tonic-gate if (read(fd, textspace, textsize) != textsize)
2807c478bd9Sstevel@tonic-gate fatal_error("cannot read text");
2817c478bd9Sstevel@tonic-gate }
2827c478bd9Sstevel@tonic-gate }
2837c478bd9Sstevel@tonic-gate
2847c478bd9Sstevel@tonic-gate if (textsize == 0)
2857c478bd9Sstevel@tonic-gate fatal_error("can't find text segment");
2867c478bd9Sstevel@tonic-gate }
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate #ifdef DEBUG
2897c478bd9Sstevel@tonic-gate static void
debug_dup_del(nltype * keeper,nltype * louser)2907c478bd9Sstevel@tonic-gate debug_dup_del(nltype * keeper, nltype * louser)
2917c478bd9Sstevel@tonic-gate {
29292ed1782Smike_s (void) printf("remove_dup_syms: discarding sym %s over sym %s\n",
293*326c1bafSToomas Soome louser->name, keeper->name);
2947c478bd9Sstevel@tonic-gate }
29592ed1782Smike_s #endif /* DEBUG */
2967c478bd9Sstevel@tonic-gate
2977c478bd9Sstevel@tonic-gate static void
remove_dup_syms(nltype * nl,sztype * sym_count)2987c478bd9Sstevel@tonic-gate remove_dup_syms(nltype *nl, sztype *sym_count)
2997c478bd9Sstevel@tonic-gate {
3007c478bd9Sstevel@tonic-gate int i;
3017c478bd9Sstevel@tonic-gate int index;
3027c478bd9Sstevel@tonic-gate int nextsym;
303*326c1bafSToomas Soome nltype *orig_list;
3047c478bd9Sstevel@tonic-gate
3057c478bd9Sstevel@tonic-gate if ((orig_list = malloc(sizeof (nltype) * *sym_count)) == NULL) {
30692ed1782Smike_s (void) fprintf(stderr,
30792ed1782Smike_s "gprof: remove_dup_syms: malloc failed\n");
30892ed1782Smike_s (void) fprintf(stderr, "Exiting due to error(s)...\n");
3097c478bd9Sstevel@tonic-gate exit(EX_UNAVAILABLE);
3107c478bd9Sstevel@tonic-gate }
31192ed1782Smike_s (void) memcpy(orig_list, nl, sizeof (nltype) * *sym_count);
3127c478bd9Sstevel@tonic-gate
3137c478bd9Sstevel@tonic-gate for (i = 0, index = 0, nextsym = 1; nextsym < *sym_count; nextsym++) {
3147c478bd9Sstevel@tonic-gate int i_type;
3157c478bd9Sstevel@tonic-gate int n_bind;
3167c478bd9Sstevel@tonic-gate int n_type;
3177c478bd9Sstevel@tonic-gate
3187c478bd9Sstevel@tonic-gate /*
3197c478bd9Sstevel@tonic-gate * If orig_list[nextsym] points to a new symvalue, then we
3207c478bd9Sstevel@tonic-gate * will copy our keeper and move on to the next symbol.
3217c478bd9Sstevel@tonic-gate */
3227c478bd9Sstevel@tonic-gate if ((orig_list + i)->value < (orig_list + nextsym)->value) {
3237c478bd9Sstevel@tonic-gate *(nl + index++) = *(orig_list +i);
3247c478bd9Sstevel@tonic-gate i = nextsym;
3257c478bd9Sstevel@tonic-gate continue;
3267c478bd9Sstevel@tonic-gate }
3277c478bd9Sstevel@tonic-gate
3287c478bd9Sstevel@tonic-gate /*
3297c478bd9Sstevel@tonic-gate * If these two symbols have the same info, then we
3307c478bd9Sstevel@tonic-gate * keep the first and keep checking for dups.
3317c478bd9Sstevel@tonic-gate */
3327c478bd9Sstevel@tonic-gate if ((orig_list + i)->syminfo ==
3337c478bd9Sstevel@tonic-gate (orig_list + nextsym)->syminfo) {
3347c478bd9Sstevel@tonic-gate DEBUG_DUP_DEL(orig_list + i, orig_list + nextsym);
3357c478bd9Sstevel@tonic-gate continue;
3367c478bd9Sstevel@tonic-gate }
3377c478bd9Sstevel@tonic-gate n_bind = ELF32_ST_BIND((orig_list + nextsym)->syminfo);
3387c478bd9Sstevel@tonic-gate i_type = ELF32_ST_TYPE((orig_list + i)->syminfo);
3397c478bd9Sstevel@tonic-gate n_type = ELF32_ST_TYPE((orig_list + nextsym)->syminfo);
3407c478bd9Sstevel@tonic-gate
3417c478bd9Sstevel@tonic-gate /*
3427c478bd9Sstevel@tonic-gate * If they have the same type we take the stronger
3437c478bd9Sstevel@tonic-gate * bound function.
3447c478bd9Sstevel@tonic-gate */
3457c478bd9Sstevel@tonic-gate if (i_type == n_type) {
3467c478bd9Sstevel@tonic-gate if (n_bind == STB_WEAK) {
3477c478bd9Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + i),
3487c478bd9Sstevel@tonic-gate (orig_list + nextsym));
3497c478bd9Sstevel@tonic-gate continue;
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + nextsym),
3527c478bd9Sstevel@tonic-gate (orig_list + i));
3537c478bd9Sstevel@tonic-gate i = nextsym;
3547c478bd9Sstevel@tonic-gate continue;
3557c478bd9Sstevel@tonic-gate }
3567c478bd9Sstevel@tonic-gate
3577c478bd9Sstevel@tonic-gate /*
3587c478bd9Sstevel@tonic-gate * If the first symbol isn't of type NOTYPE then it must
3597c478bd9Sstevel@tonic-gate * be the keeper.
3607c478bd9Sstevel@tonic-gate */
3617c478bd9Sstevel@tonic-gate if (i_type != STT_NOTYPE) {
3627c478bd9Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + i),
3637c478bd9Sstevel@tonic-gate (orig_list + nextsym));
3647c478bd9Sstevel@tonic-gate continue;
3657c478bd9Sstevel@tonic-gate }
3667c478bd9Sstevel@tonic-gate
3677c478bd9Sstevel@tonic-gate /*
3687c478bd9Sstevel@tonic-gate * Throw away the first one and take the new
3697c478bd9Sstevel@tonic-gate * symbol
3707c478bd9Sstevel@tonic-gate */
3717c478bd9Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + nextsym), (orig_list + i));
3727c478bd9Sstevel@tonic-gate i = nextsym;
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate
375*326c1bafSToomas Soome if (i < *sym_count) {
376*326c1bafSToomas Soome if ((orig_list + i)->value > (nl + index - 1)->value)
377*326c1bafSToomas Soome *(nl + index++) = *(orig_list + i);
378*326c1bafSToomas Soome }
3797c478bd9Sstevel@tonic-gate
3807c478bd9Sstevel@tonic-gate *sym_count = index;
3817c478bd9Sstevel@tonic-gate }
3827c478bd9Sstevel@tonic-gate
3837c478bd9Sstevel@tonic-gate /*
3847c478bd9Sstevel@tonic-gate * compare either by name or by value for sorting.
3857c478bd9Sstevel@tonic-gate * This is the comparison function called by qsort to
3867c478bd9Sstevel@tonic-gate * sort the symbols either by name or value when requested.
3877c478bd9Sstevel@tonic-gate */
3887c478bd9Sstevel@tonic-gate static int
compare(const void * arg1,const void * arg2)38992ed1782Smike_s compare(const void *arg1, const void *arg2)
3907c478bd9Sstevel@tonic-gate {
39192ed1782Smike_s nltype *a = (nltype *)arg1;
39292ed1782Smike_s nltype *b = (nltype *)arg2;
39392ed1782Smike_s
3947c478bd9Sstevel@tonic-gate if (a->value > b->value)
3957c478bd9Sstevel@tonic-gate return (1);
3967c478bd9Sstevel@tonic-gate else
3977c478bd9Sstevel@tonic-gate return ((a->value == b->value) - 1);
3987c478bd9Sstevel@tonic-gate }
3997c478bd9Sstevel@tonic-gate
4007c478bd9Sstevel@tonic-gate static int
is_function(Elf * elf,GElf_Sym * sym)4017c478bd9Sstevel@tonic-gate is_function(Elf *elf, GElf_Sym *sym)
4027c478bd9Sstevel@tonic-gate {
4037c478bd9Sstevel@tonic-gate Elf_Scn *scn;
4047c478bd9Sstevel@tonic-gate GElf_Shdr shdr;
4057c478bd9Sstevel@tonic-gate
4067c478bd9Sstevel@tonic-gate /*
4077c478bd9Sstevel@tonic-gate * With shared objects, it is possible we come across a function
4087c478bd9Sstevel@tonic-gate * that's global, but is undefined. The definition is probably
4097c478bd9Sstevel@tonic-gate * elsewhere, so we'll have to skip it as far as this object is
4107c478bd9Sstevel@tonic-gate * concerned.
4117c478bd9Sstevel@tonic-gate */
4127c478bd9Sstevel@tonic-gate if (sym->st_shndx == SHN_UNDEF)
4137c478bd9Sstevel@tonic-gate return (0);
4147c478bd9Sstevel@tonic-gate
4157c478bd9Sstevel@tonic-gate if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {
4167c478bd9Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL)
4177c478bd9Sstevel@tonic-gate return (1);
4187c478bd9Sstevel@tonic-gate
4197c478bd9Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) == STB_WEAK)
4207c478bd9Sstevel@tonic-gate return (1);
4217c478bd9Sstevel@tonic-gate
4227c478bd9Sstevel@tonic-gate if (!aflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)
4237c478bd9Sstevel@tonic-gate return (1);
4247c478bd9Sstevel@tonic-gate }
4257c478bd9Sstevel@tonic-gate
4267c478bd9Sstevel@tonic-gate /*
4277c478bd9Sstevel@tonic-gate * It's not a function; determine if it's in an executable section.
4287c478bd9Sstevel@tonic-gate */
4297c478bd9Sstevel@tonic-gate if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
4307c478bd9Sstevel@tonic-gate return (0);
4317c478bd9Sstevel@tonic-gate
4327c478bd9Sstevel@tonic-gate /*
4337c478bd9Sstevel@tonic-gate * If it isn't global, and it isn't weak, and it either isn't
4347c478bd9Sstevel@tonic-gate * local or the "all flag" isn't set, then get out.
4357c478bd9Sstevel@tonic-gate */
4367c478bd9Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
4377c478bd9Sstevel@tonic-gate GELF_ST_BIND(sym->st_info) != STB_WEAK &&
4387c478bd9Sstevel@tonic-gate (GELF_ST_BIND(sym->st_info) != STB_LOCAL || aflag))
4397c478bd9Sstevel@tonic-gate return (0);
4407c478bd9Sstevel@tonic-gate
4417c478bd9Sstevel@tonic-gate if (sym->st_shndx >= SHN_LORESERVE)
4427c478bd9Sstevel@tonic-gate return (0);
4437c478bd9Sstevel@tonic-gate
4447c478bd9Sstevel@tonic-gate scn = elf_getscn(elf, sym->st_shndx);
44592ed1782Smike_s (void) gelf_getshdr(scn, &shdr);
4467c478bd9Sstevel@tonic-gate
4477c478bd9Sstevel@tonic-gate if (!(shdr.sh_flags & SHF_EXECINSTR))
4487c478bd9Sstevel@tonic-gate return (0);
4497c478bd9Sstevel@tonic-gate
4507c478bd9Sstevel@tonic-gate return (1);
4517c478bd9Sstevel@tonic-gate }
4527c478bd9Sstevel@tonic-gate
4537c478bd9Sstevel@tonic-gate static void
get_symtab(Elf * elf,mod_info_t * module)45492ed1782Smike_s get_symtab(Elf *elf, mod_info_t *module)
4557c478bd9Sstevel@tonic-gate {
4567a5d89c4Sab Elf_Scn *scn = NULL, *sym_pri = NULL, *sym_aux = NULL;
4577c478bd9Sstevel@tonic-gate GElf_Word strndx = 0;
4587c478bd9Sstevel@tonic-gate sztype nsyms, i;
4597a5d89c4Sab Elf_Data *symdata_pri;
4607879e8a6SToomas Soome Elf_Data *symdata_aux = NULL;
4617879e8a6SToomas Soome GElf_Xword nsyms_pri = 0, nsyms_aux = 0;
4627c478bd9Sstevel@tonic-gate nltype *etext = NULL;
4637a5d89c4Sab nltype *l_nl, *l_npe;
4647a5d89c4Sab sztype l_nname;
4657a5d89c4Sab extern sztype total_names;
4667a5d89c4Sab int symtab_found = 0;
4677c478bd9Sstevel@tonic-gate
4687c478bd9Sstevel@tonic-gate
4697a5d89c4Sab /*
4707a5d89c4Sab * Scan the section headers looking for a symbol table. Our
4717a5d89c4Sab * preference is to use .symtab, because it contains the full
4727a5d89c4Sab * set of symbols. If we find it, we stop looking immediately
4737a5d89c4Sab * and use it. In the absence of a .symtab section, we are
4747a5d89c4Sab * willing to use the dynamic symbol table (.dynsym), possibly
4757a5d89c4Sab * augmented by the .SUNW_ldynsym, which contains local symbols.
4767a5d89c4Sab */
4777a5d89c4Sab while ((symtab_found == 0) && ((scn = elf_nextscn(elf, scn)) != NULL)) {
4787c478bd9Sstevel@tonic-gate GElf_Shdr shdr;
4797c478bd9Sstevel@tonic-gate
4807c478bd9Sstevel@tonic-gate if (gelf_getshdr(scn, &shdr) == NULL)
4817c478bd9Sstevel@tonic-gate continue;
4827c478bd9Sstevel@tonic-gate
4837a5d89c4Sab switch (shdr.sh_type) {
4847a5d89c4Sab case SHT_SYMTAB:
4857a5d89c4Sab nsyms_pri = shdr.sh_size / shdr.sh_entsize;
4867a5d89c4Sab strndx = shdr.sh_link;
4877a5d89c4Sab sym_pri = scn;
4887a5d89c4Sab /* Throw away .SUNW_ldynsym. It is for .dynsym only */
4897a5d89c4Sab nsyms_aux = 0;
4907a5d89c4Sab sym_aux = NULL;
4917a5d89c4Sab /* We have found the best symbol table. Stop looking */
4927a5d89c4Sab symtab_found = 1;
4937a5d89c4Sab break;
4947c478bd9Sstevel@tonic-gate
4957a5d89c4Sab case SHT_DYNSYM:
4967a5d89c4Sab /* We will use .dynsym if no .symtab is found */
4977a5d89c4Sab nsyms_pri = shdr.sh_size / shdr.sh_entsize;
4987c478bd9Sstevel@tonic-gate strndx = shdr.sh_link;
4997a5d89c4Sab sym_pri = scn;
5007a5d89c4Sab break;
5017c478bd9Sstevel@tonic-gate
5027a5d89c4Sab case SHT_SUNW_LDYNSYM:
5037a5d89c4Sab /* Auxiliary table, used with .dynsym */
5047a5d89c4Sab nsyms_aux = shdr.sh_size / shdr.sh_entsize;
5057a5d89c4Sab sym_aux = scn;
5067c478bd9Sstevel@tonic-gate break;
5077a5d89c4Sab }
5087c478bd9Sstevel@tonic-gate }
5097c478bd9Sstevel@tonic-gate
5107a5d89c4Sab if (sym_pri == NULL || strndx == 0)
5117c478bd9Sstevel@tonic-gate fatal_error("can't find symbol table.\n");
5127c478bd9Sstevel@tonic-gate
5137a5d89c4Sab nsyms = (sztype)(nsyms_pri + nsyms_aux);
5147a5d89c4Sab if ((nsyms_pri + nsyms_aux) != (GElf_Xword)nsyms)
5157a5d89c4Sab fatal_error(
5167a5d89c4Sab "32-bit gprof cannot handle more than 2^32 symbols");
5177a5d89c4Sab
5187a5d89c4Sab if ((symdata_pri = elf_getdata(sym_pri, NULL)) == NULL)
5197c478bd9Sstevel@tonic-gate fatal_error("can't read symbol data.\n");
5207c478bd9Sstevel@tonic-gate
5217a5d89c4Sab if ((sym_aux != NULL) &&
5227a5d89c4Sab ((symdata_aux = elf_getdata(sym_aux, NULL)) == NULL))
5237a5d89c4Sab fatal_error("can't read .SUNW_ldynsym symbol data.\n");
5247a5d89c4Sab
5257c478bd9Sstevel@tonic-gate if ((l_nl = l_npe = (nltype *)calloc(nsyms + PRF_SYMCNT,
5267c478bd9Sstevel@tonic-gate sizeof (nltype))) == NULL)
5277c478bd9Sstevel@tonic-gate fatal_error("cannot allocate symbol data.\n");
5287c478bd9Sstevel@tonic-gate
5297c478bd9Sstevel@tonic-gate /*
5307c478bd9Sstevel@tonic-gate * Now we need to cruise through the symbol table eliminating
5317c478bd9Sstevel@tonic-gate * all non-functions from consideration, and making strings
5327c478bd9Sstevel@tonic-gate * real.
5337c478bd9Sstevel@tonic-gate */
5347c478bd9Sstevel@tonic-gate l_nname = 0;
5357c478bd9Sstevel@tonic-gate
5367c478bd9Sstevel@tonic-gate for (i = 1; i < nsyms; i++) {
5377c478bd9Sstevel@tonic-gate GElf_Sym gsym;
5387c478bd9Sstevel@tonic-gate char *name;
5397c478bd9Sstevel@tonic-gate
5407a5d89c4Sab /*
5417a5d89c4Sab * Look up the symbol. In the case where we have a
5427a5d89c4Sab * .SUNW_ldynsym/.dynsym pair, we treat them as a single
5437a5d89c4Sab * logical table, with the data from .SUNW_ldynsym coming
5447a5d89c4Sab * before the data in .dynsym.
5457a5d89c4Sab */
5467a5d89c4Sab if (i >= nsyms_aux)
5477a5d89c4Sab (void) gelf_getsym(symdata_pri, i - nsyms_aux, &gsym);
5487a5d89c4Sab else
5497a5d89c4Sab (void) gelf_getsym(symdata_aux, i, &gsym);
5507c478bd9Sstevel@tonic-gate
5517c478bd9Sstevel@tonic-gate name = elf_strptr(elf, strndx, gsym.st_name);
5527c478bd9Sstevel@tonic-gate
5537c478bd9Sstevel@tonic-gate /*
5547c478bd9Sstevel@tonic-gate * We're interested in this symbol if it's a function or
5557c478bd9Sstevel@tonic-gate * if it's the symbol "_etext"
5567c478bd9Sstevel@tonic-gate */
5577c478bd9Sstevel@tonic-gate if (is_function(elf, &gsym) || strcmp(name, PRF_ETEXT) == 0) {
5587c478bd9Sstevel@tonic-gate
5597c478bd9Sstevel@tonic-gate l_npe->name = name;
5607c478bd9Sstevel@tonic-gate l_npe->value = gsym.st_value;
5617c478bd9Sstevel@tonic-gate l_npe->sz = gsym.st_size;
5627c478bd9Sstevel@tonic-gate l_npe->syminfo = gsym.st_info;
5637c478bd9Sstevel@tonic-gate l_npe->module = module;
5647c478bd9Sstevel@tonic-gate
5657c478bd9Sstevel@tonic-gate if (strcmp(name, PRF_ETEXT) == 0)
5667c478bd9Sstevel@tonic-gate etext = l_npe;
5677c478bd9Sstevel@tonic-gate
5687c478bd9Sstevel@tonic-gate if (lflag == TRUE &&
5697c478bd9Sstevel@tonic-gate GELF_ST_BIND(gsym.st_info) == STB_LOCAL) {
5707c478bd9Sstevel@tonic-gate /*
5717c478bd9Sstevel@tonic-gate * If the "locals only" flag is on, then
5727c478bd9Sstevel@tonic-gate * we add the local symbols to the
5737c478bd9Sstevel@tonic-gate * exclusion lists.
5747c478bd9Sstevel@tonic-gate */
5757c478bd9Sstevel@tonic-gate addlist(Elist, name);
5767c478bd9Sstevel@tonic-gate addlist(elist, name);
5777c478bd9Sstevel@tonic-gate }
5787c478bd9Sstevel@tonic-gate DPRINTF("Index %lld:", l_nname);
5797c478bd9Sstevel@tonic-gate DPRINTF("\tValue: 0x%llx\t", l_npe->value);
5807c478bd9Sstevel@tonic-gate DPRINTF("Name: %s \n", l_npe->name);
5817c478bd9Sstevel@tonic-gate l_npe++;
5827c478bd9Sstevel@tonic-gate l_nname++;
5837c478bd9Sstevel@tonic-gate }
5847c478bd9Sstevel@tonic-gate
5857c478bd9Sstevel@tonic-gate if (strcmp(name, PRF_END) == 0)
5867c478bd9Sstevel@tonic-gate module->data_end = gsym.st_value;
5877c478bd9Sstevel@tonic-gate }
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate if (l_npe == l_nl)
5907c478bd9Sstevel@tonic-gate fatal_error("no valid functions found");
5917c478bd9Sstevel@tonic-gate
5927c478bd9Sstevel@tonic-gate /*
5937c478bd9Sstevel@tonic-gate * Finally, we need to construct some dummy entries.
5947c478bd9Sstevel@tonic-gate */
5957c478bd9Sstevel@tonic-gate if (etext) {
5967c478bd9Sstevel@tonic-gate l_npe->name = PRF_EXTSYM;
5977c478bd9Sstevel@tonic-gate l_npe->value = etext->value + 1;
5987c478bd9Sstevel@tonic-gate l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
5997c478bd9Sstevel@tonic-gate l_npe->module = module;
6007c478bd9Sstevel@tonic-gate l_npe++;
6017c478bd9Sstevel@tonic-gate l_nname++;
6027c478bd9Sstevel@tonic-gate }
6037c478bd9Sstevel@tonic-gate
6047c478bd9Sstevel@tonic-gate l_npe->name = PRF_MEMTERM;
6057c478bd9Sstevel@tonic-gate l_npe->value = (pctype)-1;
6067c478bd9Sstevel@tonic-gate l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
6077c478bd9Sstevel@tonic-gate l_npe->module = module;
6087c478bd9Sstevel@tonic-gate l_npe++;
6097c478bd9Sstevel@tonic-gate l_nname++;
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate /*
6127c478bd9Sstevel@tonic-gate * We're almost done; all we need to do is sort the symbols
6137c478bd9Sstevel@tonic-gate * and then remove the duplicates.
6147c478bd9Sstevel@tonic-gate */
61592ed1782Smike_s qsort(l_nl, (size_t)l_nname, sizeof (nltype), compare);
6167c478bd9Sstevel@tonic-gate remove_dup_syms(l_nl, &l_nname);
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate module->nl = l_nl;
6197c478bd9Sstevel@tonic-gate module->npe = l_npe;
6207c478bd9Sstevel@tonic-gate module->nname = l_nname;
6217c478bd9Sstevel@tonic-gate
6227c478bd9Sstevel@tonic-gate total_names += l_nname;
6237c478bd9Sstevel@tonic-gate }
624