view usr/src/cmd/abi/appcert/static_prof/static_prof.c @ 0:c9caec207d52 b86

Initial porting based on b86
author Koji Uno <koji.uno@sun.com>
date Tue, 02 Jun 2009 18:56:50 +0900
parents
children 1a15d5aaf794
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1988 AT&T */
/*	  All Rights Reserved	*/

#pragma ident	"@(#)static_prof.c	1.5	05/06/24 SMI"


/* ------------------------------------------------------------------------ */
/* include headers */
/* ------------------------------------------------------------------------ */

#include "static_prof.h"

/* ========== elf_hash ==================================================== */
/*
 * DESCRIPTION:
 * The hash function copied from libelf.so.1
 */
/* ======================================================================== */

static unsigned long
my_elf_hash(const char *name)
{
	unsigned long g, h = 0;
	const unsigned char *nm = (unsigned char *) name;

	while (*nm != '\0') {
		h = (h << 4) + *nm++;
		if ((g = h & MASK) != 0)
			h ^= g >> 24;
		h &= ~MASK;
	}
	return (h);
}

/* ========== output_dtneeded ============================================= */
/*
 * DESCRIPTION:
 * Outputs all the dt_needed entries if any.
 */
/* ======================================================================== */

static void
output_dtneeded(dt_list * list)
{

	dt_list		*p = list;

	(void) fprintf(OUTPUT_FD, "#dtneeded:");
	if (!p) {
		(void) fprintf(OUTPUT_FD, "\n");
		return;
	} else {
		while (p != NULL) {
			(void) fprintf(OUTPUT_FD,
			    " %s",
			    p->libname);
			p = p->next;
		}
		(void) fprintf(OUTPUT_FD, "\n");
	}
}

/* ========== store_binding =============================================== */
/*
 * DESCRIPTION:
 * Read in the symbol binding information from the symbol table and
 * store them into the hash table of buckets.
 */
/* ======================================================================== */

static void
store_binding(binding_bucket * bind)
{
	unsigned long   bktno;
	unsigned long   orig_bktno;
	int		table_full = FALSE;
	int		i;

	bktno = my_elf_hash(bind->sym) % DEFBKTS;
	orig_bktno = bktno;

	if (!bkts[bktno].sym) {
		bkts[bktno].sym = bind->sym;
		bkts[bktno].obj = bind->obj;
		bkts[bktno].ref_lib = bind->ref_lib;
		bkts[bktno].def_lib = bind->def_lib;
		bkts[bktno].section = bind->section;
		bkts[bktno].stbind = bind->stbind;
		bkts[bktno].sttype = bind->sttype;
	} else {
		bktno = (bktno + 1) % DEFBKTS;
		for (i = bktno; i < DEFBKTS; i = (i + 1) % DEFBKTS) {
			if (i == orig_bktno) {
				table_full = TRUE;
				exit(1);
			}
			if (!bkts[i].sym)
				break;
		}
		if ((!bkts[i].sym) && (table_full != TRUE)) {
			bkts[i].sym = bind->sym;
			bkts[i].obj = bind->obj;
			bkts[i].ref_lib = bind->ref_lib;
			bkts[i].def_lib = bind->def_lib;
			bkts[i].section = bind->section;
			bkts[i].stbind = bind->stbind;
			bkts[i].sttype = bind->sttype;
		}
	}
}

/* ========== check_store_binding ========================================= */
/*
 * DESCRIPTION:
 * Check what's already on the hash table with the new symbol binding
 * information from the dependencies and record it into the bucket.
 */
/* ======================================================================== */

static void
check_store_binding(binding_bucket * bind)
{
	unsigned long   bktno;
	unsigned long   orig_bktno;
	unsigned long   i;

	bktno = my_elf_hash(bind->sym) % DEFBKTS;
	orig_bktno = bktno;

	if (!bkts[bktno].sym)
		return;
	if (bkts[bktno].sym && (strcmp(bkts[bktno].sym, bind->sym)) == 0) {
		if (strcmp(bkts[bktno].ref_lib, "<Unknown>") == 0)
			if (strcmp(bkts[bktno].obj, bind->obj))
				bkts[bktno].ref_lib = bind->obj;
	} else {
		bktno = (bktno + 1) % DEFBKTS;
		for (i = bktno; i < DEFBKTS; i = (i + 1) % DEFBKTS) {
			if (i == orig_bktno)
				break;
			if (!bkts[i].sym)
				continue;
			if (bkts[i].sym &&
			    (strcmp(bkts[i].sym, bind->sym)) == 0) {
				if (strcmp(bkts[i].ref_lib, "<Unknown>") == 0)
					if (strcmp(bkts[i].obj, bind->obj))
						bkts[i].ref_lib = bind->obj;
				break;
			}
		}
	}
}

/* ========== stringcompare =============================================== */
/*
 * DESCRIPTION:
 * Compares two strings for qsort().
 */
/* ======================================================================== */

static int
stringcompare(binding_bucket * a,
    binding_bucket * b)
{
	char		*x = "\0";
	char		*y = "\0";
	int		retcode;

	if (a->sym)
		x = a->sym;

	if (b->sym)
		y = b->sym;

	retcode = strcoll(x, y);
	return (retcode);
}

/* ========== profile_binding ============================================= */
/*
 * DESCRIPTION:
 * Output the bindings directly to stdout or a file.
 */
/* ======================================================================== */

static void
profile_binding(binding_bucket * bind)
{
	char		*ref_lib_ptr;

	if (bind->sym && strcmp(bind->ref_lib, "<Unknown>")) {
		if (ref_lib_ptr = strrchr(bind->ref_lib, (int)'/')) {
			ref_lib_ptr++;
			if (bind->stbind)
				(void) fprintf(OUTPUT_FD,
				    "%s|%s|%s|%s|%s|%s|%s\n",
				    ref_lib_ptr,
				    bind->section,
				    bind->stbind,
				    bind->sttype,
				    bind->sym,
				    bind->def_lib,
				    bind->obj);
		} else if (bind->stbind)
			(void) fprintf(OUTPUT_FD,
			    "%s|%s|%s|%s|%s|%s|%s\n",
			    bind->ref_lib,
			    bind->section,
			    bind->stbind,
			    bind->sttype,
			    bind->sym,
			    bind->def_lib,
			    bind->obj);
	} else if (bind->sym && bind->stbind)
		(void) fprintf(OUTPUT_FD,
		    "%s|%s|%s|%s|%s\n",
		    bind->obj,
		    bind->section,
		    bind->stbind,
		    bind->sttype,
		    bind->sym);
}

/* ========== output_binding ============================================== */
/*
 * DESCRIPTION:
 * Output the hash table to either stdout or a file.
 */
/* ======================================================================== */

static void
output_binding(char *prog_name,
    char *target)
{
	int		i;
	char		*ref_lib_ptr;

	qsort(bkts,
	    DEFBKTS,
	    sizeof (binding_bucket),
	    (int (*) (const void *, const void *)) stringcompare);

	if (oflag) {
		if ((OUTPUT_FD = fopen(outputfile, "w")) == NULL) {
			if (sflag)
				(void) fprintf(stderr,
				    "\nfopen failed to open <%s>...\n\n",
				    outputfile);
			exit(1);
		}
	}
	/* generates profile report */
	(void) fprintf(OUTPUT_FD,
	    "#generated by %s\n",
	    prog_name);
	(void) fprintf(OUTPUT_FD,
	    "#profiling symbols in .text section of %s\n",
	    target);
	output_dtneeded(dt_needed);

	for (i = 0; i < DEFBKTS; i++) {
		if (bkts[i].sym && strcmp(bkts[i].ref_lib, "<Unknown>")) {
			if (ref_lib_ptr = strrchr(bkts[i].ref_lib, (int)'/')) {
				ref_lib_ptr++;
				if (bkts[i].stbind)
					(void) fprintf(OUTPUT_FD,
					    "%s|%s|%s|%s|%s|%s|%s\n",
					    ref_lib_ptr,
					    bkts[i].section,
					    bkts[i].stbind,
					    bkts[i].sttype,
					    bkts[i].sym,
					    bkts[i].def_lib,
					    bkts[i].obj);
			} else if (bkts[i].stbind)
				(void) fprintf(OUTPUT_FD,
				    "%s|%s|%s|%s|%s|%s|%s\n",
				    bkts[i].ref_lib,
				    bkts[i].section,
				    bkts[i].stbind,
				    bkts[i].sttype,
				    bkts[i].sym,
				    bkts[i].def_lib,
				    bkts[i].obj);
		} else if (bkts[i].sym && bkts[i].stbind)
			(void) fprintf(OUTPUT_FD,
			    "%s|%s|%s|%s|%s\n",
			    bkts[i].obj,
			    bkts[i].section,
			    bkts[i].stbind,
			    bkts[i].sttype,
			    bkts[i].sym);
	}
}

/* ========== obj_init ==================================================== */
/*
 * DESCRIPTION:
 * Open (object) file, get ELF descriptor, and verify that the file is
 * an ELF file.
 */
/* ======================================================================== */

static int
obj_init(obj_list * c)
{
	int		mode = O_RDONLY;

	/* open the file */
	if ((c->obj->fd = open(c->obj->ename, mode)) < 0) {
		if (sflag) {
			if (errno == ENOENT)
				(void) fprintf(stderr,
				    "Cannot open <<%s>> : \
				    No such file or directory.\n",
				    c->obj->ename);
			else if (errno == EMFILE)
				(void) fprintf(stderr,
				    "File <<%s>> : Already opened.\n",
				    c->obj->ename);
		}
		c->obj->fd = NULL;
		return (FAIL);
	}
	/*
	 * queries the ELF library's internal version.
	 * Passing ver equal to EV_NONE causes elf_version() to return
	 * the library's internal version, without altering the working
	 * version.  If ver is a version known to the library,
	 * elf_version() returns the previous or initial working
	 * version number.  Otherwise, the working version remains
	 * unchanged and elf_version() returns EV_NONE.
	 */

	/* check if libelf.so is at the right level */
	if (elf_version(EV_CURRENT) == EV_NONE) {
		if (sflag)
			(void) fprintf(stderr,
			    "Library out of date in ELF access routines.\n");
		return (FAIL);
	}
	/*
	 * Before the first call to elf_begin(), it must call
	 * elf_version() to coordinate versions.
	 */

	/*
	 * get elf descriptor just to examine the contents of an existing
	 * file
	 */
	if ((c->obj->elf = elf_begin(c->obj->fd, ELF_C_READ, (Elf *) 0))
	    == (Elf *) 0) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not in executable and \
			    linking format(ELF).\n");
		return (FAIL);
	}
	/* Rule out COFF, a.out and shell script files */
	if (elf_kind(c->obj->elf) == ELF_K_COFF) {
		if (sflag) {
			(void) fprintf(stderr,
			    "File is not in executable \
			    and linking format(ELF) or archive.\n");
		}
		return (FAIL);
	}
	if (elf_kind(c->obj->elf) != ELF_K_AR &&
	    elf_kind(c->obj->elf) != ELF_K_ELF) {
		if (sflag) {
			(void) fprintf(stderr,
			    "File is not in executable and linking \
			    format(ELF) or archive.\n");
		}
		return (FAIL);
	}
	return (SUCCEED);
}

/* ========== obj_elf_hdr ================================================= */
/*
 * DESCRIPTION:
 * Obtain the elf header, verify elf header information
 */
/* ======================================================================== */

static int
obj_elf_hdr(obj_list * c)
{
#if	defined(_LP64)
	Elf64_Ehdr	*ptr;
#else
	Elf32_Ehdr	*ptr;
#endif

	/*
	 * get the elf header if one is available for the ELF descriptor
	 * c->elf
	 */
#if	defined(_LP64)
	if ((ptr = elf64_getehdr(c->obj->elf)) == (Elf64_Ehdr *) 0) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not in 64-bit format.\n");
		return (FAIL);
	}
#else
	if ((ptr = elf32_getehdr(c->obj->elf)) == (Elf32_Ehdr *) 0) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not in 32-bit format.\n");
		return (FAIL);
	}
#endif

	/* if there is elf header, save the pointer */
#if defined(_LP64)
	c->obj->ehdr = (Elf64_Ehdr *) ptr;
#else
	c->obj->ehdr = (Elf32_Ehdr *) ptr;
#endif

	/* e_ident[] is identification index which holds values */
	/*
	 * we could also use elf_getident() to retrieve file identification
	 * data.
	 */

	/*
	 * e_ident[EI_CLASS] identifies the file's class:
	 * ELFCLASSNONE - invalid class
	 * ELFCLASS32   - 32-bit objects
	 * ELFCLASS64   - 64-bit objects
	 */

#if	defined(_LP64)
	if (ptr->e_ident[EI_CLASS] != ELFCLASS64) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not in 64-bit format.\n");
		return (FAIL);
	}
#else
	if (ptr->e_ident[EI_CLASS] != ELFCLASS32) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not in 32-bit format.\n");
		return (FAIL);
	}
#endif
	/*
	 * e_ident[EI_DATA] specifies the data encoding of the
	 * processor-specific data in the object file:
	 * ELFDATANONE - invalid data encoding
	 * ELFDATA2LSB - specifies 2's complement values, with the least
	 * significant byte occupying the lowest address
	 * ELFDATA2MSB - specifies 2's complement values, with the most
	 * significant byte occupying the lowest address
	 */

	/*
	 * e_ident[EI_VERSION] specifies the ELF header version number.
	 * Currently, this value must be EV_CURRENT.
	 */

	if (!(ptr->e_ident[EI_VERSION] == EV_CURRENT) &&
	    (ptr->e_version == EV_CURRENT)) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is recorded in an \
			    incompatible ELF version.\n");
		return (FAIL);
	}
	/* only interested in relocatable, shared object, or executable file */
	switch (ptr->e_type) {
	case ET_REL:
	case ET_EXEC:
	case ET_DYN:
		break;
	default:
		if (sflag) {
			(void) fprintf(stderr,
			    "File is not relocatable, ");
			(void) fprintf(stderr,
			    "executable, or shared object.\n");
		}
		return (FAIL);
	}

	/*
	 * e_machine's value specifies the required architecture for an
	 * individual file
	 */

#if defined(__sparcv9)
	if (ptr->e_machine != EM_SPARCV9) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not for 64-bit \
			    SPARC machine architecture.\n");
		return (FAIL);
	}
#elif defined(__amd64)
	if (ptr->e_machine != EM_AMD64) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not for 64-bit \
			    amd64 machine architecture.\n");
		return (FAIL);
	}
#elif defined(__i386)
	if (ptr->e_machine != EM_386) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not for 32-bit \
			    i386 machine architecture.\n");
		return (FAIL);
	}
#else
	if (ptr->e_machine != EM_SPARC) {
		if (sflag)
			(void) fprintf(stderr,
			    "File is not for 32-bit \
			    SPARC machine architecture.\n");
		return (FAIL);
	}
#endif
	return (SUCCEED);
}

/* ========== obj_prog_hdr ============================================= */
/*
 * DESCRIPTION:
 * For executable files and shared objects only, check if it has
 * a program header table.
 */
/* ===================================================================== */

static int
obj_prog_hdr(obj_list * c)
{
	/*
	 * Assume:  the elf header has already been read, and the file
	 * has already been determined to be
	 * executable, shared object, or relocatable
	 */

	/*
	 * Program headers are meaningful only for executable and shared
	 * object files.  It is an array of structures, each describing a
	 * segment or other information needs to prepare the program for
	 * execution.
	 */

	/* skip if file is not executable or shared object */
	/* e_type == ET_REL meaning Relocatable file */
	if (c->obj->ehdr->e_type == ET_REL)
		return (SUCCEED);

	/*
	 * ehdr->e_phoff holds the program header table's file offset in
	 * bytes.
	 */
	/* If the file has no program header table, this member holds zero. */
	/*
	 * ehdr->e_phnum holds the number of entries in the program header
	 * table.
	 */
	/*
	 * If a file has no program header table, e_phnum holds the value
	 * zero.
	 */

	/* make sure there's a program header table */
	if ((c->obj->ehdr->e_phoff == 0) ||
	    (c->obj->ehdr->e_phnum == 0)) {
		if (sflag)
			(void) fprintf(stderr,
			    "File has no program header table.\n");
		return (FAIL);
	}
	return (SUCCEED);
}

/* ========== find_dynamic_sect ========================================== */
/*
 * DESCRIPTION:
 * Find the dynamic section.
 */
/* ======================================================================= */

static int
find_dynamic_sect(obj_list * c)
{
#if	defined(_LP64)
	Elf64_Shdr	*scurrent;	/* temp 64 bit section pointer */
#else
	Elf32_Shdr	*scurrent;	/* temp 32 bit section pointer */
#endif
	Elf_Scn		*scn;	/* temp section header pointer */
	Elf_Data	*ddata;	/* temp data header pointer */
	size_t		index;	/* temp section header table index */

	c->obj->dynnames = NULL; /* init of dynamic string table ptr */
	c->obj->dynsect = NULL;	/* init of dynamic section ptr */
	c->obj->ddata = NULL;	/* init of dynamic strtab data ptr */

	/* only process executables and shared objects */
	if (c->obj->ehdr->e_type != ET_EXEC && c->obj->ehdr->e_type != ET_DYN)
		return (SUCCEED);

	if ((c->obj->ehdr->e_shoff == 0) || (c->obj->ehdr->e_shnum == 0)) {
		/* there are no sections */
		return (SUCCEED);
	}
	/* search the section header table for dynamic section */

	/* start with null section; section index = 0 */
	scn = 0;

	while ((scn = elf_nextscn(c->obj->elf, scn)) != 0) {
		/* retrieve the section header */
#if	defined(_LP64)
		scurrent = elf64_getshdr(scn);
#else
		scurrent = elf32_getshdr(scn);
#endif

		/* check for dynamic section; (i.e., .dynamic) */
		if (scurrent->sh_type == SHT_DYNAMIC) {
			ddata = 0;
			if ((ddata = elf_getdata(scn, ddata)) == 0 ||
			    (ddata->d_size == 0))
				return (SUCCEED);

			/* now, we got data of dynamic section */
			c->obj->dynsect = ddata->d_buf;

			/* index to section header for dynamic string table */
			index = scurrent->sh_link;
			/* get scn descriptor of dynamic string table */
			scn = elf_getscn(c->obj->elf, index);
			/* get dynamic string table section header */
#if	defined(_LP64)
			scurrent = elf64_getshdr(scn);
#else
			scurrent = elf32_getshdr(scn);
#endif
			/* get the dynamic string table data descriptor */
			c->obj->ddata = elf_getdata(scn, (c->obj->ddata));
			/* save the pointer to dynamic string table data */
			c->obj->dynnames = c->obj->ddata->d_buf;
			/*
			 * now, we got dynamic strtab and dynamic section
			 * information
			 */
			break;
		}
	}
	return (SUCCEED);
}

/* ========== find_symtabs ================================================ */
/*
 * DESCRIPTION:
 * Find and check symbol tables for an application file
 */
/* ======================================================================== */

static int
find_symtabs(obj_list * c)
{
#if	defined(_LP64)
	Elf64_Shdr	*shdr;
#else
	Elf32_Shdr	*shdr;
#endif
	Elf_Scn		*scn, *scn2;
	Elf_Data	*data;

	c->obj->sym_tab = NULL;
	c->obj->sym_num = 0;
	c->obj->sym_names = NULL;
	c->obj->dsym_tab = NULL;
	c->obj->dsym_num = 0;
	c->obj->dsym_names = NULL;
	c->obj->sym_data = NULL;
	c->obj->dsym_data = NULL;
	scn = 0;

	/*
	 * loop through the section header table looking for symbol tables.
	 * There must be one or two:  .symtab and .dynsym
	 * upon finding a symbol table, save its pointer in obj_com.
	 */

	/* get section descriptor */
	while ((scn = elf_nextscn(c->obj->elf, scn)) != 0) {
#if	defined(_LP64)
		Elf64_Sym	*syms;
#else
		Elf32_Sym	*syms;
#endif
		int		symn;
		char		*strs;

		/* point to section header */
#if	defined(_LP64)
		shdr = elf64_getshdr(scn);
#else
		shdr = elf32_getshdr(scn);
#endif

		if (shdr == 0)
			return (FAIL);

		/* skip if this section is not a symbol table */
		if ((shdr->sh_type != SHT_DYNSYM) &&
		    (shdr->sh_type != SHT_SYMTAB))
			continue;

		/* get data descriptor for the symbol table itself */
		data = elf_getdata(scn, NULL);
		if (data == NULL)
			continue;

		/* save pointer to symbol table */
#if	defined(_LP64)
		syms = (Elf64_Sym *) data->d_buf;
#else
		syms = (Elf32_Sym *) data->d_buf;
#endif

		/*
		 * now start looking for the string table associated with
		 * this symbol table section
		 */

		/* get section descriptor first */
		scn2 = elf_getscn(c->obj->elf, shdr->sh_link);
		if (scn2 == NULL)
			continue;

		/* get data descriptor for the string table section */
		data = elf_getdata(scn2, NULL);
		if (data == NULL)
			continue;

		/* save pointer to name string table */
		strs = data->d_buf;
		symn = shdr->sh_size / shdr->sh_entsize;

		/* save information in obj_com */
		if (shdr->sh_type == SHT_SYMTAB) {
			c->obj->sym_tab = syms;
			c->obj->sym_num = symn;
			c->obj->sym_names = strs;
			c->obj->sym_data = data;
		} else {	/* must be the dynamic linking symbol table */
			c->obj->dsym_tab = syms;
			c->obj->dsym_num = symn;
			c->obj->dsym_names = strs;
			c->obj->dsym_data = data;
		}		/* end if */
	}			/* end while */
	return (SUCCEED);
}

/* ========== obj_app_symtab ============================================== */
/*
 * DESCRIPTION:
 * Check existence of application's symbol tables.
 */
/* ======================================================================== */

static int
obj_app_symtab(obj_list * c)
{
	/* issue error if a relocatable file has no symbol table */
	if (c->obj->sym_tab == NULL) {
		if (c->obj->ehdr->e_type == ET_REL) {
			if (sflag)
				(void) fprintf(stderr,
				    "ELF error: no symbol \
				    table in object file.\n");
			return (FAIL);
		} else {
			if (c->obj->dsym_tab == NULL) {
				if (sflag) {
					(void) fprintf(stderr,
					    "Warning: Binary is \
					    completely statically \
					    linked and stripped.\n");
				}
				return (FAIL);
			}
			if (sflag)
				(void) fprintf(stderr,
				    "Binary is stripped.\n");
		}
	}
	return (SUCCEED);
}

/* ========== obj_finis =================================================== */
/*
 * DESCRIPTION:
 * It checks the c->fd and c->elf pointers.  If they are not NULL,
 * close the file descriptor and ELF descriptor.
 */
/* ======================================================================== */

static void
obj_finis(obj_list * c)
{
	obj_list	*p;

	if (c) {
		while (c) {
			if (c->obj->elf != (Elf *) 0)
				(void) elf_end(c->obj->elf);
			if (c->obj->fd != 0)
				(void) close(c->obj->fd);
			p = c;
			c = c->next;
			free(p->obj);
			free(p);
		}
	}
}

/* ========= is_text_section ============================================== */
/*
 * DESCRIPTION:
 * Scan through every section and returns TRUE(1) if the given section
 * is ".text", otherwise, returns FALSE(0).
 * INPUTS:        shndx - section header index
 * elf_file - ELF descriptor of the object file under test
 * ehdr - ELF header of the object file under test
 */
/* ======================================================================== */

static int
is_text_section(int shndx,
    Elf * elf_file,
#if	defined(_LP64)
    Elf64_Ehdr * ehdr)
#else
    Elf32_Ehdr * ehdr)
#endif
{
	char		*sym_name;
	Elf_Scn		*scn = elf_getscn(elf_file, shndx);

	if (scn != NULL) {
#if	defined(_LP64)
		Elf64_Shdr	*shdr;
		shdr = elf64_getshdr(scn);
#else
		Elf32_Shdr	*shdr;
		shdr = elf32_getshdr(scn);
#endif
		sym_name = elf_strptr(elf_file,
				    ehdr->e_shstrndx,
				    shdr->sh_name);
		if (strcmp(sym_name, ".text") == 0)
			return (1);
	}
	return (0);
}

/* ========== scan_archive_symbols ======================================= */
/*
 * DESCRIPTION:
 * Scan through the archive symbol tables and write them out.
 * INPUTS:        syms - pointer to application symbol table
 * symn - number of entries in application symbol table
 * buf  - first byte of application string table
 */
/* ======================================================================= */

static void
scan_archive_symbols(obj_list * c,
#if	defined(_LP64)
    Elf64_Sym * syms,
#else
    Elf32_Sym * syms,
#endif
    int symn,
    char *buf,
    Elf * elf_file,
#if	defined(_LP64)
    Elf64_Ehdr * ehdr)
#else
    Elf32_Ehdr * ehdr)
#endif
{
#if	defined(_LP64)
	Elf64_Sym	*symtab_entry;
#else
	Elf32_Sym	*symtab_entry;
#endif
	int		i;
	char		*sym_name;
	int		sttype;
	int		stbind;

	symtab_entry = syms;
	for (i = 0; i < symn; i++, symtab_entry++) {
		binding_bucket *binding;
		/* look only at .text section symbols */
		if (!is_text_section(symtab_entry->st_shndx, elf_file, ehdr))
			continue;

		/* look only at weak and global symbols */
#if	defined(_LP64)
		stbind = ELF64_ST_BIND(symtab_entry->st_info);
#else
		stbind = ELF32_ST_BIND(symtab_entry->st_info);
#endif
		if (stbind != STB_GLOBAL) {
			if (stbind != STB_WEAK)
				continue;
		}
		/* look only at functions and objects */
#if	defined(_LP64)
		sttype = ELF64_ST_TYPE(symtab_entry->st_info);
#else
		sttype = ELF32_ST_TYPE(symtab_entry->st_info);
#endif
		if (sttype != STT_FUNC) {
			if (sttype != STT_OBJECT)
				continue;
		}
		sym_name = buf + symtab_entry->st_name;
		binding = (struct binding_bucket *)
			    malloc(sizeof (binding_bucket));
		binding->sym = sym_name;
		binding->obj = c->obj->ename;
		binding->section = "TEXT";
		binding->ref_lib = "<Unknown>";
		binding->def_lib = "*DIRECT*";
		if (stbind == STB_GLOBAL)
			binding->stbind = "GLOB";
		else if (stbind == STB_WEAK)
			binding->stbind = "WEAK";
		if (sttype == STT_FUNC)
			binding->sttype = "FUNC";
		else if (sttype == STT_OBJECT)
			binding->sttype = "OBJT";
		if (pflag)
			profile_binding(binding);
		else
			store_binding(binding);
	}			/* end for */
}

/* ========== scan_symbols ================================================ */
/*
 * DESCRIPTION:
 * Scan through the symbol table and write them out.
 * INPUTS:        syms - pointer to application symbol table
 * symn - number of entries in application symbol table
 * buf  - first byte of application string table
 */
/* ======================================================================== */

static void
scan_symbols(obj_list * c,
#if	defined(_LP64)
    Elf64_Sym * syms,
#else
    Elf32_Sym * syms,
#endif
    int symn,
    char *buf)
{
#if	defined(_LP64)
	Elf64_Sym	*symtab_entry;
#else
	Elf32_Sym	*symtab_entry;
#endif
	int		i;
	char		*sym_name;
	int		sttype;
	int		stbind;

	symtab_entry = syms;
	if (pflag) {
		(void) fprintf(OUTPUT_FD,
		    "#profiling symbols in .text section of %s\n",
		    c->obj->ename);
		output_dtneeded(dt_needed);
	}
	for (i = 0; i < symn; i++, symtab_entry++) {
		binding_bucket *binding;
		/* look only at .text section symbols */
		if (!is_text_section(symtab_entry->st_shndx,
				    c->obj->elf,
				    c->obj->ehdr))
			continue;

		/* look only at weak and global symbols */
#if	defined(_LP64)
		stbind = ELF64_ST_BIND(symtab_entry->st_info);
#else
		stbind = ELF32_ST_BIND(symtab_entry->st_info);
#endif
		if (stbind != STB_GLOBAL) {
			if (stbind != STB_WEAK)
				continue;
		}
		/* look only at functions and objects */
#if	defined(_LP64)
		sttype = ELF64_ST_TYPE(symtab_entry->st_info);
#else
		sttype = ELF32_ST_TYPE(symtab_entry->st_info);
#endif
		if (sttype != STT_FUNC) {
			if (sttype != STT_OBJECT)
				continue;
		}
		sym_name = buf + symtab_entry->st_name;
		binding = (struct binding_bucket *)
			    malloc(sizeof (binding_bucket));
		binding->sym = sym_name;
		binding->obj = c->obj->ename;
		binding->section = "TEXT";
		binding->ref_lib = "<Unknown>";
		binding->def_lib = "*DIRECT*";
		if (stbind == STB_GLOBAL)
			binding->stbind = "GLOB";
		else if (stbind == STB_WEAK)
			binding->stbind = "WEAK";
		if (sttype == STT_FUNC)
			binding->sttype = "FUNC";
		else if (sttype == STT_OBJECT)
			binding->sttype = "OBJT";
		if (pflag)
			profile_binding(binding);
		else
			store_binding(binding);
	}			/* end for */
}

/* ========= bind_symbols ================================================= */
/*
 * DESCRIPTION:
 * Scan through the dynamic symbol table and write them out.
 * INPUTS:        syms - pointer to application symbol table
 * symn - number of entries in application symbol table
 * buf  - first byte of application string table
 */
/* ======================================================================== */

static void
bind_symbols(obj_list * c,
#if	defined(_LP64)
    Elf64_Sym * syms,
#else
    Elf32_Sym * syms,
#endif
    int symn,
    char *buf)
{
#if	defined(_LP64)
	Elf64_Sym	*symtab_entry;
#else
	Elf32_Sym	*symtab_entry;
#endif
	int		i;
	char		*sym_name;
	binding_bucket	*binding;
	int		sttype;
	int		stbind;

	symtab_entry = syms;
	for (i = 0; i < symn; i++, symtab_entry++) {
		/* look only at global symbols */
#if	defined(_LP64)
		stbind = ELF64_ST_BIND(symtab_entry->st_info);
#else
		stbind = ELF32_ST_BIND(symtab_entry->st_info);
#endif
		if (symtab_entry->st_shndx == SHN_UNDEF)
			continue;
		if (symtab_entry->st_shndx == SHN_ABS)
			continue;
		if (stbind != STB_GLOBAL) {
			if (stbind != STB_WEAK)
				continue;
		}
		/* look only at functions and objects */
#if	defined(_LP64)
		sttype = ELF64_ST_TYPE(symtab_entry->st_info);
#else
		sttype = ELF32_ST_TYPE(symtab_entry->st_info);
#endif
		if (sttype != STT_FUNC) {
			if (sttype != STT_OBJECT)
				continue;
		}
		sym_name = buf + symtab_entry->st_name;
		binding = (binding_bucket *) malloc(sizeof (binding_bucket));
		binding->obj = c->obj->ename;
		binding->sym = sym_name;
		if (!pflag)
			check_store_binding(binding);
	}			/* end for */
}

/* ========== get_scnfd =================================================== */
/*
 * DESCRIPTION:
 * Gets section descriptor for the associated string table
 * and verifies that the type of the section pointed to is
 * indeed of type STRTAB.  Returns a valid section descriptor
 * or NULL on error.
 */
/* ======================================================================== */

static Elf_Scn *
get_scnfd(Elf * e_file,
    int shstrtab,
    int SCN_TYPE)
{
	Elf_Scn		*scn_fd;
#if	defined(_LP64)
	Elf64_Shdr	*shdr;
#else
	Elf32_Shdr	*shdr;
#endif

	if ((scn_fd = elf_getscn(e_file, shstrtab)) == NULL)
		return (NULL);

#if	defined(_LP64)
	shdr = elf64_getshdr(scn_fd);
#else
	shdr = elf32_getshdr(scn_fd);
#endif

	if (shdr->sh_type != SCN_TYPE)
		return (NULL);
	return (scn_fd);
}

/* ========== print_symtab ================================================ */
/*
 * DESCRIPTION:
 * Outputs symbol bindings from symbol table to hash table.
 */
/* ======================================================================== */

static void
print_symtab(obj_list * com,
    Elf * elf_file,
#if	defined(_LP64)
    Elf64_Ehdr * ehdr,
    Elf64_Shdr * shdr,
#else
    Elf32_Ehdr * ehdr,
    Elf32_Shdr * shdr,
#endif
    Elf_Scn * p_sd,
    char *filename)
{
#if	defined(_LP64)
	Elf64_Sym	*syms;
#else
	Elf32_Sym	*syms;
#endif
	Elf_Data	*data;
	Elf_Scn		*scn;
	int		count = 0;
	char		*strs, *fullname;
	obj_list	*c;

	c = (obj_list *) malloc(sizeof (obj_list));
	c->obj = (obj_com *) malloc(sizeof (obj_com));
	fullname = (char *)malloc(strlen(com->obj->ename)
			+ strlen(filename) + 2);
	(void *) strcpy(fullname, com->obj->ename);
	(void *) strcat(fullname, "(");
	(void *) strcat(fullname, filename);
	(void *) strcat(fullname, ")");
	c->obj->ename = fullname;

	if ((data = elf_getdata(p_sd, NULL)) == NULL) {
		if (sflag)
			(void) fprintf(stderr,
			    "%s - No symbol table data\n",
			    c->obj->ename);
		return;
	}
#if	defined(_LP64)
	syms = (Elf64_Sym *) data->d_buf;
#else
	syms = (Elf32_Sym *) data->d_buf;
#endif

	scn = elf_getscn(elf_file, shdr->sh_link);
	if (scn == NULL)
		return;
	data = elf_getdata(scn, NULL);
	if (data == NULL)
		return;
	strs = data->d_buf;
	count = shdr->sh_size / shdr->sh_entsize;
	if (syms == NULL) {
		if (sflag)
			(void) fprintf(stderr,
			    "%s: Problem reading symbol data\n",
			    c->obj->ename);
		return;
	}
	c->obj->sym_tab = syms;
	c->obj->sym_num = count;
	c->obj->sym_names = strs;

	if (aflag)
		(void) scan_archive_symbols(c,
		    c->obj->sym_tab,
		    c->obj->sym_num,
		    c->obj->sym_names,
		    elf_file,
		    ehdr);
	else
		(void) bind_symbols(c,
		    c->obj->sym_tab,
		    c->obj->sym_num,
		    c->obj->sym_names);
	free(c->obj);
	free(c);
}

/* ========== get_symtab ================================================== */
/*
 * DESCRIPTION:
 * Gets the symbol table.  This function does not output the contents
 * of the symbol table but sets up the parameters and then calls
 * print_symtab() to output the symbol bindings.
 */
/* ======================================================================== */

static void
get_symtab(obj_list * c,
    Elf * elf_file,
#if	defined(_LP64)
    Elf64_Ehdr * ehdr,
#else
    Elf32_Ehdr * ehdr,
#endif
    char *filename)
{
	Elf_Scn		*scn, *scnfd;
	Elf_Data	*data;
#if	defined(_LP64)
	Elf64_Word	symtabtype;
#else
	Elf32_Word	symtabtype;
#endif

	/* get section header string table */
	scnfd = get_scnfd(elf_file, ehdr->e_shstrndx, SHT_STRTAB);
	if (scnfd == NULL) {
		if (sflag)
			(void) fprintf(stderr,
			    "%s: Could not get string table\n",
			    filename);
		return;
	}
	data = elf_getdata(scnfd, NULL);
	if (data->d_size == 0) {
		if (sflag)
			(void) fprintf(stderr,
			    "%s: No data in string table\n",
			    filename);
		return;
	}
	symtabtype = SHT_SYMTAB;
	scn = 0;
	while ((scn = elf_nextscn(elf_file, scn)) != 0) {
#if	defined(_LP64)
		Elf64_Shdr	*shdr;
		if ((shdr = elf64_getshdr(scn)) == NULL)
#else
		Elf32_Shdr	*shdr;
		if ((shdr = elf32_getshdr(scn)) == NULL)
#endif
		{
			if (sflag)
				(void) fprintf(stderr,
				    "%s: %s:\n",
				    filename,
				    elf_errmsg(-1));
			return;
		}
		if (shdr->sh_type == symtabtype)
			print_symtab(c, elf_file, ehdr, shdr, scn, filename);
	}			/* end while */
}

/* ========== process ===================================================== */
/*
 * DESCRIPTION:
 * Gets the ELF header and, if it exists, call get_symtab() to begin
 * processing of the file; otherwise, returns with a warning.
 */
/* ======================================================================== */

static void
process(obj_list * c,
    Elf * elf_file,
    char *filename)
{
#if	defined(_LP64)
	Elf64_Ehdr	*ehdr;
#else
	Elf32_Ehdr	*ehdr;
#endif

#if	defined(_LP64)
	if ((ehdr = elf64_getehdr(elf_file)) == NULL)
#else
	if ((ehdr = elf32_getehdr(elf_file)) == NULL)
#endif
	{
		if (sflag)
			(void) fprintf(stderr,
			    "%s: %s\n",
			    filename, elf_errmsg(-1));
		return;
	}
	get_symtab(c, elf_file, ehdr, filename);
}

/* ========== process_archive ============================================= */
/*
 * DESCRIPTION:
 * Processes member files of an archive.  This function provides
 * a loop through an archive equivalent the processing of each_file
 * for individual object file.
 */
/* ======================================================================== */

static int
process_archive(obj_list * c)
{
	Elf_Arhdr	*p_ar;
	Elf		*arf;
	Elf_Cmd		cmd = ELF_C_READ;

	while ((arf = elf_begin(c->obj->fd, cmd, c->obj->elf)) != 0) {
		p_ar = elf_getarhdr(arf);
		if (p_ar == NULL) {
			if (sflag)
				(void) fprintf(stderr,
				    "%s: %s\n",
				    c->obj->filename, elf_errmsg(-1));
			return (FAIL);
		}
		if ((int)strncmp(p_ar->ar_name, "/", 1) == 0) {
			cmd = elf_next(arf);
			(void) elf_end(arf);
			continue;
		}
		if (elf_kind(arf) == ELF_K_ELF) {
			process(c, arf, p_ar->ar_name);
		} else {
			cmd = elf_next(arf);
			(void) elf_end(arf);
			continue;
		}
		cmd = elf_next(arf);
		(void) elf_end(arf);
	}			/* end while */
	return (SUCCEED);
}

/* ========== add_dtneeded ================================================ */
/*
 * DESCRIPTION:
 * Inserts a new node into the linked list.  It is basically for
 * generating a simple linked list of DT_NEEDED entries.
 */
/* ======================================================================== */

static dt_list *
add_dtneeded(dt_list * p,
    dt_list * node)
{
	dt_list		*head = p, *tail;

	if (!head)
		head = node;
	else {
		tail = head;
		if (strcmp(tail->libname, node->libname) == 0) {
			free(node);
			return (head);
		}
		while (tail->next != NULL) {
			tail = tail->next;
			if (strcmp(tail->libname, node->libname) == 0) {
				free(node);
				return (head);
			}
		}
		tail->next = node;
	}
	return (head);
}

/* ========== find_dtneeded =============================================== */
/*
 * DESCRIPTION:
 * Find the DT_NEEDED, DT_FILTER, and DT_AUXILIARY entries, and save
 * them to link list.
 */
/* ======================================================================== */

static void
find_dtneeded(obj_list * c)
{
#if	defined(_LP64)
	Elf64_Dyn	*dcurrent; /* temp 64 bit dynamic table entry ptr */
#else
	Elf32_Dyn	*dcurrent; /* temp 32 bit dynamic table entry ptr */
#endif
	dt_list		*tmp_lib;

	dcurrent = c->obj->dynsect;
	if (!dcurrent)
		return;

	/*
	 * If there are any DT_NEEDED
	 * entries, add them to the dt_needed list.
	 */

	while (dcurrent->d_tag != DT_NULL) {
		if (dcurrent->d_tag == DT_NEEDED) {
			tmp_lib = (dt_list *) malloc(sizeof (dt_list));
			tmp_lib->libname = c->obj->dynnames +
					    dcurrent->d_un.d_val;
			tmp_lib->d_tag = dcurrent->d_tag;
			tmp_lib->next = NULL;
			dt_needed = add_dtneeded(dt_needed, tmp_lib);
		}
		dcurrent++;
	}
}

/* ========= obj_elfcheck ================================================= */
/*
 * DESCRIPTION:
 * It checks the elf header and saves its pointer if succeeds.
 * It checks the program header and saves its pointer if succeed.
 * It checks the section header table and saves its pointer to
 * section header table and section header string table if it
 * succeeds.  It finds dynsym symbol table and saves its pointer.
 * It finds symtab and saves its pointers.
 */
/* ======================================================================== */

static int
obj_elfcheck(obj_list * c)
{
	/* open the file and ELF descriptor */
	if (obj_init(c) == FAIL) {
		obj_finis(c);
		return (FAIL);
	}
	/* if it is an archive library */
	if (elf_kind(c->obj->elf) == ELF_K_AR) {
		if (process_archive(c) == SUCCEED)
			return (SUCCEED);
		else
			return (FAIL);
	}
	/* get the ELF header information */
	if (obj_elf_hdr(c) == FAIL) {
		obj_finis(c);
		return (FAIL);
	}
	/* get the program header for dynamic, etc. */
	if (obj_prog_hdr(c) == FAIL) {
		obj_finis(c);
		return (FAIL);
	}
	/* find and save pointers to application symbol tables */
	if (find_symtabs(c) == FAIL) {
		obj_finis(c);
		return (FAIL);
	}
	/* check the existence of application's symbol tables */
	if (obj_app_symtab(c) == FAIL) {
		obj_finis(c);
		return (FAIL);
	}
	/* find and save pointers to the dynamic section */
	if (find_dynamic_sect(c) == FAIL) {
		obj_finis(c);
		return (FAIL);
	}
	/*
	 * find the DT_NEEDED entries and save the name to dt_needed link
	 * list
	 */
	(void) find_dtneeded(c);

	return (SUCCEED);
}

/* ========= analyze_dependency ========================================== */
/*
 * DESCRIPTION:
 * Read in an dependency object file and analyze it.
 * INPUTS:        dep_file - dependency object file name
 */
/* ======================================================================= */

static int
analyze_dependency(char *dep_file)
{
	obj_list	*dep_obj;

	if (!dep_file)
		return (SUCCEED);

	dep_obj = (obj_list *) malloc(sizeof (obj_list));
	(void) memset(dep_obj, 0, sizeof (obj_list));
	dep_obj->obj = (obj_com *) malloc(sizeof (obj_com));
	(void) memset(dep_obj->obj, 0, sizeof (obj_com));
	dep_obj->next = NULL;
	dep_obj->obj->filename = dep_file;
	dep_obj->obj->ename = dep_obj->obj->filename;

	if (obj_elfcheck(dep_obj) == FAIL)
		return (FAIL);

	if (dep_obj->obj->dsym_names != NULL)
		bind_symbols(dep_obj,
		    dep_obj->obj->dsym_tab,
		    dep_obj->obj->dsym_num,
		    dep_obj->obj->dsym_names);

	if (dep_obj->obj->sym_names != NULL)
		bind_symbols(dep_obj,
		    dep_obj->obj->sym_tab,
		    dep_obj->obj->sym_num,
		    dep_obj->obj->sym_names);
	return (SUCCEED);
}

/* ========= analyze_main =============================================== */
/*
 * DESCRIPTION:
 * Read in an object file and analyze it.
 */
/* ====================================================================== */

static void
analyze_main(obj_list * c)
{
	int	i;

	if (obj_elfcheck(c) == FAIL)
		exit(1);

	aflag = FALSE;

	if (c->obj->sym_names != NULL)
		scan_symbols(c,
		    c->obj->sym_tab,
		    c->obj->sym_num,
		    c->obj->sym_names);
	else if (c->obj->dsym_names != NULL)
		scan_symbols(c,
		    c->obj->dsym_tab,
		    c->obj->dsym_num,
		    c->obj->dsym_names);

	if (c->obj->numfiles == 0)
		return;

	for (i = 0; i < c->obj->numfiles; i++)
		(void) analyze_dependency(c->obj->filenames[i]);
}

/* ========= analyze_args ================================================= */
/*
 * DESCRIPTION:
 * Analyze the command-line options.
 */
/* ======================================================================== */

static int
analyze_args(obj_list * c,
    int argc,
    char *argv[])
{
	extern char	*optarg;
	extern int	optind;
	int		option;
	int		i;
	char		*nameptr;
	char		slash = '/';
	int		errflg = 0;

	if ((nameptr = strrchr(argv[0], slash)) != NULL)
		nameptr++;
	else
		nameptr = argv[0];

	while ((option = getopt(argc, argv, "pso:a")) != EOF) {
		switch (option) {
		case 'p':	/* just do profiling; write to stdout */
			pflag = 1;
			break;
		case 's':	/* silent mode to turn off stderr messages */
			sflag = 0;
			break;
		case 'o':	/* redirects the output */
			outputfile = optarg;
			oflag = 1;
			break;
		case 'a':	/* processes archive as input */
			aflag = 1;
			break;
		case '?':
		default:
			errflg++;
		}		/* end switch */
	}			/* end while */

	/* exit if there are no files to process */
	if (optind >= argc)
		errflg++;
	if (errflg) {
		(void) fprintf(stderr,
		    "usage: %s [-p] [-s] [-o outputfile] ", nameptr);
		(void) fprintf(stderr,
		    "<archive>|<binary_executable>\n");
		(void) fprintf(stderr,
		    "\t\t   [<archive>|<dynamic library>...]\n");
		return (FALSE);
	}			/* end if */
	c->obj->filename = argv[optind++];
	c->obj->ename = c->obj->filename;

	/* compute number of files and save their pointers */
	c->obj->numfiles = argc - optind;

	if (c->obj->numfiles > 0) {
		i = 0;
		c->obj->filenames = (char **)
				    malloc(sizeof (char *) *
				    (c->obj->numfiles + 1));
		for (; optind < argc; i++, optind++)
			c->obj->filenames[i] = argv[optind];
	}
	return (TRUE);
}

/* ======================================================================= */
/*
 * Here starts the main ()
 */
/* ======================================================================= */

int
main(int argc, char *argv[])
{
	obj_list	*main_obj;
	dt_list		*q;

	main_obj = (obj_list *) malloc(sizeof (obj_list));
	(void) memset(main_obj, 0, sizeof (obj_list));
	main_obj->obj = (obj_com *) malloc(sizeof (obj_com));
	(void) memset(main_obj->obj, 0, sizeof (obj_com));
	main_obj->next = NULL;

	if (!analyze_args(main_obj, argc, argv))
		exit(1);

	if (oflag && pflag) {
		if ((OUTPUT_FD = fopen(outputfile, "w")) == NULL) {
			if (sflag)
				(void) fprintf(stderr,
				    "\nfopen failed to open <%s>...\n\n",
				    outputfile);
			exit(1);
		}
	}
	/* generates profile report if pflag is set */
	if (pflag)
		(void) fprintf(OUTPUT_FD,
		    "#generated by %s\n",
		    argv[0]);

	/* analyze the input file */
	analyze_main(main_obj);

	/* generates profile report */
	if (!pflag)
		output_binding(argv[0], main_obj->obj->ename);

	/* close the library .so file descriptor and ELF descriptor */
	obj_finis(main_obj);

	/* de-allocates the dt_needed link list */
	if (dt_needed) {
		while (dt_needed) {
			q = dt_needed;
			dt_needed = dt_needed->next;
			free(q);
		}
	}
	/* close the output redirect file descriptor */
	if (oflag)
		(void) fclose(OUTPUT_FD);

	return (0);
}