10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*3621Sab196087 * Common Development and Distribution License (the "License").
6*3621Sab196087 * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
21211Smike_s
220Sstevel@tonic-gate /*
23*3621Sab196087 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24211Smike_s * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
280Sstevel@tonic-gate
290Sstevel@tonic-gate #include "gprof.h"
300Sstevel@tonic-gate #include <stdlib.h>
310Sstevel@tonic-gate #include <sys/file.h>
320Sstevel@tonic-gate #include <fcntl.h>
330Sstevel@tonic-gate #include <unistd.h>
340Sstevel@tonic-gate #include <string.h>
350Sstevel@tonic-gate #include <sysexits.h>
360Sstevel@tonic-gate #include <libelf.h>
370Sstevel@tonic-gate #include "gelf.h"
380Sstevel@tonic-gate
390Sstevel@tonic-gate #ifdef DEBUG
400Sstevel@tonic-gate static void debug_dup_del(nltype *, nltype *);
410Sstevel@tonic-gate
420Sstevel@tonic-gate #define DPRINTF(msg, file) if (debug & ELFDEBUG) \
43211Smike_s (void) printf(msg, file);
440Sstevel@tonic-gate
450Sstevel@tonic-gate #define PRINTF(msg) if (debug & ELFDEBUG) \
46211Smike_s (void) printf(msg);
470Sstevel@tonic-gate
480Sstevel@tonic-gate #define DEBUG_DUP_DEL(keeper, louser) if (debug & ELFDEBUG) \
490Sstevel@tonic-gate debug_dup_del(keeper, louser);
500Sstevel@tonic-gate
510Sstevel@tonic-gate #else
520Sstevel@tonic-gate #define DPRINTF(msg, file)
530Sstevel@tonic-gate #define PRINTF(msg)
540Sstevel@tonic-gate #define DEBUG_DUP_DEL(keeper, louser)
550Sstevel@tonic-gate #endif
560Sstevel@tonic-gate
570Sstevel@tonic-gate size_t textbegin, textsize;
580Sstevel@tonic-gate
590Sstevel@tonic-gate /* Prototype definitions first */
600Sstevel@tonic-gate
610Sstevel@tonic-gate static void process(char *filename, int fd);
62211Smike_s static void get_symtab(Elf *elf, mod_info_t *module);
63211Smike_s static void get_textseg(Elf *elf, int fd);
640Sstevel@tonic-gate static void save_aout_info(char *);
650Sstevel@tonic-gate
660Sstevel@tonic-gate static void
fatal_error(char * error)670Sstevel@tonic-gate fatal_error(char *error)
680Sstevel@tonic-gate {
69211Smike_s (void) fprintf(stderr,
70211Smike_s "Fatal ELF error: %s (%s)\n", error, elf_errmsg(-1));
710Sstevel@tonic-gate exit(EX_SOFTWARE);
720Sstevel@tonic-gate }
730Sstevel@tonic-gate
740Sstevel@tonic-gate bool
is_shared_obj(char * name)750Sstevel@tonic-gate is_shared_obj(char *name)
760Sstevel@tonic-gate {
770Sstevel@tonic-gate int fd;
780Sstevel@tonic-gate Elf *elf;
790Sstevel@tonic-gate GElf_Ehdr ehdr;
800Sstevel@tonic-gate
810Sstevel@tonic-gate if ((fd = open(name, O_RDONLY)) == -1) {
82211Smike_s (void) fprintf(stderr, "%s: can't open `%s'\n", whoami, name);
830Sstevel@tonic-gate exit(EX_NOINPUT);
840Sstevel@tonic-gate }
850Sstevel@tonic-gate
860Sstevel@tonic-gate if (elf_version(EV_CURRENT) == EV_NONE)
870Sstevel@tonic-gate fatal_error("libelf is out of date");
880Sstevel@tonic-gate
890Sstevel@tonic-gate if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
900Sstevel@tonic-gate fatal_error("can't read as ELF file");
910Sstevel@tonic-gate
920Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL)
930Sstevel@tonic-gate fatal_error("can't read ehdr");
940Sstevel@tonic-gate
95211Smike_s (void) elf_end(elf);
96211Smike_s (void) close(fd);
970Sstevel@tonic-gate
980Sstevel@tonic-gate if (ehdr.e_type == ET_DYN)
990Sstevel@tonic-gate return (TRUE);
1000Sstevel@tonic-gate else
1010Sstevel@tonic-gate return (FALSE);
1020Sstevel@tonic-gate }
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate static void
save_aout_info(char * aoutname)1050Sstevel@tonic-gate save_aout_info(char *aoutname)
1060Sstevel@tonic-gate {
1070Sstevel@tonic-gate struct stat buf;
1080Sstevel@tonic-gate extern fl_info_t aout_info;
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate if (stat(aoutname, &buf) == -1) {
111211Smike_s (void) fprintf(stderr, "%s: can't get info on `%s'\n",
1120Sstevel@tonic-gate whoami, aoutname);
1130Sstevel@tonic-gate exit(EX_NOINPUT);
1140Sstevel@tonic-gate }
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate aout_info.dev = buf.st_dev;
1170Sstevel@tonic-gate aout_info.ino = buf.st_ino;
1180Sstevel@tonic-gate aout_info.mtime = buf.st_mtime;
1190Sstevel@tonic-gate aout_info.size = buf.st_size;
1200Sstevel@tonic-gate }
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate void
getnfile(char * aoutname)1230Sstevel@tonic-gate getnfile(char *aoutname)
1240Sstevel@tonic-gate {
1250Sstevel@tonic-gate int fd;
1260Sstevel@tonic-gate
1270Sstevel@tonic-gate DPRINTF(" Attempting to open %s \n", aoutname);
1280Sstevel@tonic-gate if ((fd = open((aoutname), O_RDONLY)) == -1) {
1290Sstevel@tonic-gate (void) fprintf(stderr, "%s: can't open `%s'\n",
1300Sstevel@tonic-gate whoami, aoutname);
1310Sstevel@tonic-gate exit(EX_NOINPUT);
1320Sstevel@tonic-gate }
1330Sstevel@tonic-gate process(aoutname, fd);
1340Sstevel@tonic-gate save_aout_info(aoutname);
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate (void) close(fd);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate static GElf_Addr
get_txtorigin(Elf * elf)1400Sstevel@tonic-gate get_txtorigin(Elf *elf)
1410Sstevel@tonic-gate {
1420Sstevel@tonic-gate GElf_Ehdr ehdr;
1430Sstevel@tonic-gate GElf_Phdr phdr;
1440Sstevel@tonic-gate GElf_Half ndx;
1450Sstevel@tonic-gate GElf_Addr txt_origin = 0;
1460Sstevel@tonic-gate bool first_load_seg = TRUE;
1470Sstevel@tonic-gate
1480Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL)
1490Sstevel@tonic-gate fatal_error("can't read ehdr");
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate for (ndx = 0; ndx < ehdr.e_phnum; ndx++) {
1520Sstevel@tonic-gate if (gelf_getphdr(elf, ndx, &phdr) == NULL)
1530Sstevel@tonic-gate continue;
1540Sstevel@tonic-gate
1550Sstevel@tonic-gate if ((phdr.p_type == PT_LOAD) && !(phdr.p_flags & PF_W)) {
1560Sstevel@tonic-gate if (first_load_seg || phdr.p_vaddr < txt_origin)
1570Sstevel@tonic-gate txt_origin = phdr.p_vaddr;
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate if (first_load_seg)
1600Sstevel@tonic-gate first_load_seg = FALSE;
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate }
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate return (txt_origin);
1650Sstevel@tonic-gate }
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate void
process_namelist(mod_info_t * module)1680Sstevel@tonic-gate process_namelist(mod_info_t *module)
1690Sstevel@tonic-gate {
1700Sstevel@tonic-gate int fd;
1710Sstevel@tonic-gate Elf *elf;
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate if ((fd = open(module->name, O_RDONLY)) == -1) {
1740Sstevel@tonic-gate (void) fprintf(stderr, "%s: can't read %s\n",
1750Sstevel@tonic-gate whoami, module->name);
176211Smike_s (void) fprintf(stderr, "Exiting due to error(s)...\n");
1770Sstevel@tonic-gate exit(EX_NOINPUT);
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate /*
1810Sstevel@tonic-gate * libelf's version already verified in processing a.out,
1820Sstevel@tonic-gate * so directly do elf_begin()
1830Sstevel@tonic-gate */
1840Sstevel@tonic-gate if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
1850Sstevel@tonic-gate fatal_error("can't read as ELF file");
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate module->next = NULL;
1880Sstevel@tonic-gate module->txt_origin = get_txtorigin(elf);
189211Smike_s get_symtab(elf, module);
1900Sstevel@tonic-gate module->active = TRUE;
1910Sstevel@tonic-gate }
1920Sstevel@tonic-gate
1930Sstevel@tonic-gate /*
1940Sstevel@tonic-gate * Get the ELF header and, if it exists, call get_symtab()
1950Sstevel@tonic-gate * to begin processing of the file; otherwise, return from
1960Sstevel@tonic-gate * processing the file with a warning.
1970Sstevel@tonic-gate */
1980Sstevel@tonic-gate static void
process(char * filename,int fd)1990Sstevel@tonic-gate process(char *filename, int fd)
2000Sstevel@tonic-gate {
2010Sstevel@tonic-gate Elf *elf;
2020Sstevel@tonic-gate extern bool cflag;
2030Sstevel@tonic-gate extern bool Bflag;
2040Sstevel@tonic-gate
2050Sstevel@tonic-gate if (elf_version(EV_CURRENT) == EV_NONE)
2060Sstevel@tonic-gate fatal_error("libelf is out of date");
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
2090Sstevel@tonic-gate fatal_error("can't read as ELF file");
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate if (gelf_getclass(elf) == ELFCLASS64)
2120Sstevel@tonic-gate Bflag = TRUE;
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate /*
2150Sstevel@tonic-gate * Initialize active modules list. Note that we set the end
2160Sstevel@tonic-gate * address while reading the symbol table, in get_symtab
2170Sstevel@tonic-gate */
2180Sstevel@tonic-gate modules.id = 1;
2190Sstevel@tonic-gate modules.next = NULL;
2200Sstevel@tonic-gate modules.txt_origin = get_txtorigin(elf);
2210Sstevel@tonic-gate modules.load_base = modules.txt_origin;
222211Smike_s if ((modules.name = malloc(strlen(filename) + 1)) == NULL) {
223211Smike_s (void) fprintf(stderr, "%s: can't malloc %d bytes",
2240Sstevel@tonic-gate whoami, strlen(filename) + 1);
2250Sstevel@tonic-gate exit(EX_UNAVAILABLE);
2260Sstevel@tonic-gate }
227211Smike_s (void) strcpy(modules.name, filename);
2280Sstevel@tonic-gate
229211Smike_s get_symtab(elf, &modules);
2300Sstevel@tonic-gate
2310Sstevel@tonic-gate modules.load_end = modules.data_end;
2320Sstevel@tonic-gate modules.active = TRUE;
2330Sstevel@tonic-gate n_modules = 1;
2340Sstevel@tonic-gate
2350Sstevel@tonic-gate if (cflag)
236211Smike_s get_textseg(elf, fd);
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate static void
get_textseg(Elf * elf,int fd)240211Smike_s get_textseg(Elf *elf, int fd)
2410Sstevel@tonic-gate {
2420Sstevel@tonic-gate GElf_Ehdr ehdr;
2430Sstevel@tonic-gate GElf_Phdr phdr;
2440Sstevel@tonic-gate GElf_Half i;
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL)
2470Sstevel@tonic-gate fatal_error("can't read ehdr");
2480Sstevel@tonic-gate
2490Sstevel@tonic-gate for (i = 0; i < ehdr.e_phnum; i++) {
2500Sstevel@tonic-gate
2510Sstevel@tonic-gate if (gelf_getphdr(elf, i, &phdr) == NULL)
2520Sstevel@tonic-gate continue;
2530Sstevel@tonic-gate
2540Sstevel@tonic-gate if (!(phdr.p_flags & PF_W) && (phdr.p_filesz > textsize)) {
2550Sstevel@tonic-gate size_t chk;
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate /*
2580Sstevel@tonic-gate * We could have multiple loadable text segments;
2590Sstevel@tonic-gate * keep the largest we find.
2600Sstevel@tonic-gate */
2610Sstevel@tonic-gate if (textspace)
2620Sstevel@tonic-gate free(textspace);
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate /*
2650Sstevel@tonic-gate * gprof is a 32-bit program; if this text segment
2660Sstevel@tonic-gate * has a > 32-bit offset or length, it's too big.
2670Sstevel@tonic-gate */
2680Sstevel@tonic-gate chk = (size_t)phdr.p_vaddr + (size_t)phdr.p_filesz;
2690Sstevel@tonic-gate if (phdr.p_vaddr + phdr.p_filesz != (GElf_Xword)chk)
2700Sstevel@tonic-gate fatal_error("text segment too large for -c");
2710Sstevel@tonic-gate
2720Sstevel@tonic-gate textbegin = (size_t)phdr.p_vaddr;
2730Sstevel@tonic-gate textsize = (size_t)phdr.p_filesz;
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate textspace = malloc(textsize);
2760Sstevel@tonic-gate
2770Sstevel@tonic-gate if (lseek(fd, (off_t)phdr.p_offset, SEEK_SET) !=
2780Sstevel@tonic-gate (off_t)phdr.p_offset)
2790Sstevel@tonic-gate fatal_error("cannot seek to text section");
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate if (read(fd, textspace, textsize) != textsize)
2820Sstevel@tonic-gate fatal_error("cannot read text");
2830Sstevel@tonic-gate }
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate
2860Sstevel@tonic-gate if (textsize == 0)
2870Sstevel@tonic-gate fatal_error("can't find text segment");
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate #ifdef DEBUG
2910Sstevel@tonic-gate static void
debug_dup_del(nltype * keeper,nltype * louser)2920Sstevel@tonic-gate debug_dup_del(nltype * keeper, nltype * louser)
2930Sstevel@tonic-gate {
294211Smike_s (void) printf("remove_dup_syms: discarding sym %s over sym %s\n",
2950Sstevel@tonic-gate louser->name, keeper->name);
2960Sstevel@tonic-gate }
297211Smike_s #endif /* DEBUG */
2980Sstevel@tonic-gate
2990Sstevel@tonic-gate static void
remove_dup_syms(nltype * nl,sztype * sym_count)3000Sstevel@tonic-gate remove_dup_syms(nltype *nl, sztype *sym_count)
3010Sstevel@tonic-gate {
3020Sstevel@tonic-gate int i;
3030Sstevel@tonic-gate int index;
3040Sstevel@tonic-gate int nextsym;
3050Sstevel@tonic-gate
3060Sstevel@tonic-gate nltype * orig_list;
3070Sstevel@tonic-gate if ((orig_list = malloc(sizeof (nltype) * *sym_count)) == NULL) {
308211Smike_s (void) fprintf(stderr,
309211Smike_s "gprof: remove_dup_syms: malloc failed\n");
310211Smike_s (void) fprintf(stderr, "Exiting due to error(s)...\n");
3110Sstevel@tonic-gate exit(EX_UNAVAILABLE);
3120Sstevel@tonic-gate }
313211Smike_s (void) memcpy(orig_list, nl, sizeof (nltype) * *sym_count);
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate for (i = 0, index = 0, nextsym = 1; nextsym < *sym_count; nextsym++) {
3160Sstevel@tonic-gate int i_type;
3170Sstevel@tonic-gate int n_bind;
3180Sstevel@tonic-gate int n_type;
3190Sstevel@tonic-gate
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate * If orig_list[nextsym] points to a new symvalue, then we
3220Sstevel@tonic-gate * will copy our keeper and move on to the next symbol.
3230Sstevel@tonic-gate */
3240Sstevel@tonic-gate if ((orig_list + i)->value < (orig_list + nextsym)->value) {
3250Sstevel@tonic-gate *(nl + index++) = *(orig_list +i);
3260Sstevel@tonic-gate i = nextsym;
3270Sstevel@tonic-gate continue;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate * If these two symbols have the same info, then we
3320Sstevel@tonic-gate * keep the first and keep checking for dups.
3330Sstevel@tonic-gate */
3340Sstevel@tonic-gate if ((orig_list + i)->syminfo ==
3350Sstevel@tonic-gate (orig_list + nextsym)->syminfo) {
3360Sstevel@tonic-gate DEBUG_DUP_DEL(orig_list + i, orig_list + nextsym);
3370Sstevel@tonic-gate continue;
3380Sstevel@tonic-gate }
3390Sstevel@tonic-gate n_bind = ELF32_ST_BIND((orig_list + nextsym)->syminfo);
3400Sstevel@tonic-gate i_type = ELF32_ST_TYPE((orig_list + i)->syminfo);
3410Sstevel@tonic-gate n_type = ELF32_ST_TYPE((orig_list + nextsym)->syminfo);
3420Sstevel@tonic-gate
3430Sstevel@tonic-gate /*
3440Sstevel@tonic-gate * If they have the same type we take the stronger
3450Sstevel@tonic-gate * bound function.
3460Sstevel@tonic-gate */
3470Sstevel@tonic-gate if (i_type == n_type) {
3480Sstevel@tonic-gate if (n_bind == STB_WEAK) {
3490Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + i),
3500Sstevel@tonic-gate (orig_list + nextsym));
3510Sstevel@tonic-gate continue;
3520Sstevel@tonic-gate }
3530Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + nextsym),
3540Sstevel@tonic-gate (orig_list + i));
3550Sstevel@tonic-gate i = nextsym;
3560Sstevel@tonic-gate continue;
3570Sstevel@tonic-gate }
3580Sstevel@tonic-gate
3590Sstevel@tonic-gate /*
3600Sstevel@tonic-gate * If the first symbol isn't of type NOTYPE then it must
3610Sstevel@tonic-gate * be the keeper.
3620Sstevel@tonic-gate */
3630Sstevel@tonic-gate if (i_type != STT_NOTYPE) {
3640Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + i),
3650Sstevel@tonic-gate (orig_list + nextsym));
3660Sstevel@tonic-gate continue;
3670Sstevel@tonic-gate }
3680Sstevel@tonic-gate
3690Sstevel@tonic-gate /*
3700Sstevel@tonic-gate * Throw away the first one and take the new
3710Sstevel@tonic-gate * symbol
3720Sstevel@tonic-gate */
3730Sstevel@tonic-gate DEBUG_DUP_DEL((orig_list + nextsym), (orig_list + i));
3740Sstevel@tonic-gate i = nextsym;
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate
3770Sstevel@tonic-gate if ((orig_list + i)->value > (nl + index - 1)->value)
3780Sstevel@tonic-gate *(nl + index++) = *(orig_list +i);
3790Sstevel@tonic-gate
3800Sstevel@tonic-gate *sym_count = index;
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate
3830Sstevel@tonic-gate /*
3840Sstevel@tonic-gate * compare either by name or by value for sorting.
3850Sstevel@tonic-gate * This is the comparison function called by qsort to
3860Sstevel@tonic-gate * sort the symbols either by name or value when requested.
3870Sstevel@tonic-gate */
3880Sstevel@tonic-gate static int
compare(const void * arg1,const void * arg2)389211Smike_s compare(const void *arg1, const void *arg2)
3900Sstevel@tonic-gate {
391211Smike_s nltype *a = (nltype *)arg1;
392211Smike_s nltype *b = (nltype *)arg2;
393211Smike_s
3940Sstevel@tonic-gate if (a->value > b->value)
3950Sstevel@tonic-gate return (1);
3960Sstevel@tonic-gate else
3970Sstevel@tonic-gate return ((a->value == b->value) - 1);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate
4000Sstevel@tonic-gate static int
is_function(Elf * elf,GElf_Sym * sym)4010Sstevel@tonic-gate is_function(Elf *elf, GElf_Sym *sym)
4020Sstevel@tonic-gate {
4030Sstevel@tonic-gate Elf_Scn *scn;
4040Sstevel@tonic-gate GElf_Shdr shdr;
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate /*
4070Sstevel@tonic-gate * With shared objects, it is possible we come across a function
4080Sstevel@tonic-gate * that's global, but is undefined. The definition is probably
4090Sstevel@tonic-gate * elsewhere, so we'll have to skip it as far as this object is
4100Sstevel@tonic-gate * concerned.
4110Sstevel@tonic-gate */
4120Sstevel@tonic-gate if (sym->st_shndx == SHN_UNDEF)
4130Sstevel@tonic-gate return (0);
4140Sstevel@tonic-gate
4150Sstevel@tonic-gate if (GELF_ST_TYPE(sym->st_info) == STT_FUNC) {
4160Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) == STB_GLOBAL)
4170Sstevel@tonic-gate return (1);
4180Sstevel@tonic-gate
4190Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) == STB_WEAK)
4200Sstevel@tonic-gate return (1);
4210Sstevel@tonic-gate
4220Sstevel@tonic-gate if (!aflag && GELF_ST_BIND(sym->st_info) == STB_LOCAL)
4230Sstevel@tonic-gate return (1);
4240Sstevel@tonic-gate }
4250Sstevel@tonic-gate
4260Sstevel@tonic-gate /*
4270Sstevel@tonic-gate * It's not a function; determine if it's in an executable section.
4280Sstevel@tonic-gate */
4290Sstevel@tonic-gate if (GELF_ST_TYPE(sym->st_info) != STT_NOTYPE)
4300Sstevel@tonic-gate return (0);
4310Sstevel@tonic-gate
4320Sstevel@tonic-gate /*
4330Sstevel@tonic-gate * If it isn't global, and it isn't weak, and it either isn't
4340Sstevel@tonic-gate * local or the "all flag" isn't set, then get out.
4350Sstevel@tonic-gate */
4360Sstevel@tonic-gate if (GELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
4370Sstevel@tonic-gate GELF_ST_BIND(sym->st_info) != STB_WEAK &&
4380Sstevel@tonic-gate (GELF_ST_BIND(sym->st_info) != STB_LOCAL || aflag))
4390Sstevel@tonic-gate return (0);
4400Sstevel@tonic-gate
4410Sstevel@tonic-gate if (sym->st_shndx >= SHN_LORESERVE)
4420Sstevel@tonic-gate return (0);
4430Sstevel@tonic-gate
4440Sstevel@tonic-gate scn = elf_getscn(elf, sym->st_shndx);
445211Smike_s (void) gelf_getshdr(scn, &shdr);
4460Sstevel@tonic-gate
4470Sstevel@tonic-gate if (!(shdr.sh_flags & SHF_EXECINSTR))
4480Sstevel@tonic-gate return (0);
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate return (1);
4510Sstevel@tonic-gate }
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate static void
get_symtab(Elf * elf,mod_info_t * module)454211Smike_s get_symtab(Elf *elf, mod_info_t *module)
4550Sstevel@tonic-gate {
456*3621Sab196087 Elf_Scn *scn = NULL, *sym_pri = NULL, *sym_aux = NULL;
4570Sstevel@tonic-gate GElf_Word strndx = 0;
4580Sstevel@tonic-gate sztype nsyms, i;
459*3621Sab196087 Elf_Data *symdata_pri;
460*3621Sab196087 Elf_Data *symdata_aux;
461*3621Sab196087 GElf_Xword nsyms_pri, nsyms_aux = 0;
4620Sstevel@tonic-gate nltype *etext = NULL;
463*3621Sab196087 nltype *l_nl, *l_npe;
464*3621Sab196087 sztype l_nname;
465*3621Sab196087 extern sztype total_names;
466*3621Sab196087 int symtab_found = 0;
467*3621Sab196087
4680Sstevel@tonic-gate
469*3621Sab196087 /*
470*3621Sab196087 * Scan the section headers looking for a symbol table. Our
471*3621Sab196087 * preference is to use .symtab, because it contains the full
472*3621Sab196087 * set of symbols. If we find it, we stop looking immediately
473*3621Sab196087 * and use it. In the absence of a .symtab section, we are
474*3621Sab196087 * willing to use the dynamic symbol table (.dynsym), possibly
475*3621Sab196087 * augmented by the .SUNW_ldynsym, which contains local symbols.
476*3621Sab196087 */
477*3621Sab196087 while ((symtab_found == 0) && ((scn = elf_nextscn(elf, scn)) != NULL)) {
4780Sstevel@tonic-gate GElf_Shdr shdr;
4790Sstevel@tonic-gate
4800Sstevel@tonic-gate if (gelf_getshdr(scn, &shdr) == NULL)
4810Sstevel@tonic-gate continue;
4820Sstevel@tonic-gate
483*3621Sab196087 switch (shdr.sh_type) {
484*3621Sab196087 case SHT_SYMTAB:
485*3621Sab196087 nsyms_pri = shdr.sh_size / shdr.sh_entsize;
486*3621Sab196087 strndx = shdr.sh_link;
487*3621Sab196087 sym_pri = scn;
488*3621Sab196087 /* Throw away .SUNW_ldynsym. It is for .dynsym only */
489*3621Sab196087 nsyms_aux = 0;
490*3621Sab196087 sym_aux = NULL;
491*3621Sab196087 /* We have found the best symbol table. Stop looking */
492*3621Sab196087 symtab_found = 1;
493*3621Sab196087 break;
4940Sstevel@tonic-gate
495*3621Sab196087 case SHT_DYNSYM:
496*3621Sab196087 /* We will use .dynsym if no .symtab is found */
497*3621Sab196087 nsyms_pri = shdr.sh_size / shdr.sh_entsize;
4980Sstevel@tonic-gate strndx = shdr.sh_link;
499*3621Sab196087 sym_pri = scn;
500*3621Sab196087 break;
5010Sstevel@tonic-gate
502*3621Sab196087 case SHT_SUNW_LDYNSYM:
503*3621Sab196087 /* Auxiliary table, used with .dynsym */
504*3621Sab196087 nsyms_aux = shdr.sh_size / shdr.sh_entsize;
505*3621Sab196087 sym_aux = scn;
5060Sstevel@tonic-gate break;
507*3621Sab196087 }
5080Sstevel@tonic-gate }
5090Sstevel@tonic-gate
510*3621Sab196087 if (sym_pri == NULL || strndx == 0)
5110Sstevel@tonic-gate fatal_error("can't find symbol table.\n");
5120Sstevel@tonic-gate
513*3621Sab196087 nsyms = (sztype)(nsyms_pri + nsyms_aux);
514*3621Sab196087 if ((nsyms_pri + nsyms_aux) != (GElf_Xword)nsyms)
515*3621Sab196087 fatal_error(
516*3621Sab196087 "32-bit gprof cannot handle more than 2^32 symbols");
517*3621Sab196087
518*3621Sab196087 if ((symdata_pri = elf_getdata(sym_pri, NULL)) == NULL)
5190Sstevel@tonic-gate fatal_error("can't read symbol data.\n");
5200Sstevel@tonic-gate
521*3621Sab196087 if ((sym_aux != NULL) &&
522*3621Sab196087 ((symdata_aux = elf_getdata(sym_aux, NULL)) == NULL))
523*3621Sab196087 fatal_error("can't read .SUNW_ldynsym symbol data.\n");
524*3621Sab196087
5250Sstevel@tonic-gate if ((l_nl = l_npe = (nltype *)calloc(nsyms + PRF_SYMCNT,
5260Sstevel@tonic-gate sizeof (nltype))) == NULL)
5270Sstevel@tonic-gate fatal_error("cannot allocate symbol data.\n");
5280Sstevel@tonic-gate
5290Sstevel@tonic-gate /*
5300Sstevel@tonic-gate * Now we need to cruise through the symbol table eliminating
5310Sstevel@tonic-gate * all non-functions from consideration, and making strings
5320Sstevel@tonic-gate * real.
5330Sstevel@tonic-gate */
5340Sstevel@tonic-gate l_nname = 0;
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate for (i = 1; i < nsyms; i++) {
5370Sstevel@tonic-gate GElf_Sym gsym;
5380Sstevel@tonic-gate char *name;
5390Sstevel@tonic-gate
540*3621Sab196087 /*
541*3621Sab196087 * Look up the symbol. In the case where we have a
542*3621Sab196087 * .SUNW_ldynsym/.dynsym pair, we treat them as a single
543*3621Sab196087 * logical table, with the data from .SUNW_ldynsym coming
544*3621Sab196087 * before the data in .dynsym.
545*3621Sab196087 */
546*3621Sab196087 if (i >= nsyms_aux)
547*3621Sab196087 (void) gelf_getsym(symdata_pri, i - nsyms_aux, &gsym);
548*3621Sab196087 else
549*3621Sab196087 (void) gelf_getsym(symdata_aux, i, &gsym);
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate name = elf_strptr(elf, strndx, gsym.st_name);
5520Sstevel@tonic-gate
5530Sstevel@tonic-gate /*
5540Sstevel@tonic-gate * We're interested in this symbol if it's a function or
5550Sstevel@tonic-gate * if it's the symbol "_etext"
5560Sstevel@tonic-gate */
5570Sstevel@tonic-gate if (is_function(elf, &gsym) || strcmp(name, PRF_ETEXT) == 0) {
5580Sstevel@tonic-gate
5590Sstevel@tonic-gate l_npe->name = name;
5600Sstevel@tonic-gate l_npe->value = gsym.st_value;
5610Sstevel@tonic-gate l_npe->sz = gsym.st_size;
5620Sstevel@tonic-gate l_npe->syminfo = gsym.st_info;
5630Sstevel@tonic-gate l_npe->module = module;
5640Sstevel@tonic-gate
5650Sstevel@tonic-gate if (strcmp(name, PRF_ETEXT) == 0)
5660Sstevel@tonic-gate etext = l_npe;
5670Sstevel@tonic-gate
5680Sstevel@tonic-gate if (lflag == TRUE &&
5690Sstevel@tonic-gate GELF_ST_BIND(gsym.st_info) == STB_LOCAL) {
5700Sstevel@tonic-gate /*
5710Sstevel@tonic-gate * If the "locals only" flag is on, then
5720Sstevel@tonic-gate * we add the local symbols to the
5730Sstevel@tonic-gate * exclusion lists.
5740Sstevel@tonic-gate */
5750Sstevel@tonic-gate addlist(Elist, name);
5760Sstevel@tonic-gate addlist(elist, name);
5770Sstevel@tonic-gate }
5780Sstevel@tonic-gate DPRINTF("Index %lld:", l_nname);
5790Sstevel@tonic-gate DPRINTF("\tValue: 0x%llx\t", l_npe->value);
5800Sstevel@tonic-gate DPRINTF("Name: %s \n", l_npe->name);
5810Sstevel@tonic-gate l_npe++;
5820Sstevel@tonic-gate l_nname++;
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate if (strcmp(name, PRF_END) == 0)
5860Sstevel@tonic-gate module->data_end = gsym.st_value;
5870Sstevel@tonic-gate }
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate if (l_npe == l_nl)
5900Sstevel@tonic-gate fatal_error("no valid functions found");
5910Sstevel@tonic-gate
5920Sstevel@tonic-gate /*
5930Sstevel@tonic-gate * Finally, we need to construct some dummy entries.
5940Sstevel@tonic-gate */
5950Sstevel@tonic-gate if (etext) {
5960Sstevel@tonic-gate l_npe->name = PRF_EXTSYM;
5970Sstevel@tonic-gate l_npe->value = etext->value + 1;
5980Sstevel@tonic-gate l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
5990Sstevel@tonic-gate l_npe->module = module;
6000Sstevel@tonic-gate l_npe++;
6010Sstevel@tonic-gate l_nname++;
6020Sstevel@tonic-gate }
6030Sstevel@tonic-gate
6040Sstevel@tonic-gate l_npe->name = PRF_MEMTERM;
6050Sstevel@tonic-gate l_npe->value = (pctype)-1;
6060Sstevel@tonic-gate l_npe->syminfo = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
6070Sstevel@tonic-gate l_npe->module = module;
6080Sstevel@tonic-gate l_npe++;
6090Sstevel@tonic-gate l_nname++;
6100Sstevel@tonic-gate
6110Sstevel@tonic-gate /*
6120Sstevel@tonic-gate * We're almost done; all we need to do is sort the symbols
6130Sstevel@tonic-gate * and then remove the duplicates.
6140Sstevel@tonic-gate */
615211Smike_s qsort(l_nl, (size_t)l_nname, sizeof (nltype), compare);
6160Sstevel@tonic-gate remove_dup_syms(l_nl, &l_nname);
6170Sstevel@tonic-gate
6180Sstevel@tonic-gate module->nl = l_nl;
6190Sstevel@tonic-gate module->npe = l_npe;
6200Sstevel@tonic-gate module->nname = l_nname;
6210Sstevel@tonic-gate
6220Sstevel@tonic-gate total_names += l_nname;
6230Sstevel@tonic-gate }
624