xref: /illumos-gate/usr/src/cmd/sgs/gprof/common/readelf.c (revision 326c1baf)
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