xref: /onnv-gate/usr/src/cmd/sgs/gprof/common/readelf.c (revision 3621:2cbc0f92c696)
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