view usr/src/cmd/fs.d/cachefs/cachefspack/elfrd.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 (c) 1996-1997, by Sun Microsystems, Inc.
 * All Rights Reserved.
 */

#pragma ident	"@(#)elfrd.c	1.11	05/06/08 SMI"

#include <locale.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <stdio.h>
#include <fcntl.h>
#include <link.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <search.h>

#include "libelf.h"
#include "elfrd.h"

extern int verbose;
extern char *mstrdup(const char *);
extern void *mmalloc(size_t size);

/*
 * Given the name of an executable and a function call the function for
 * all shared objects needed to link the executable. The function will only
 * be called once. A list of filenames for which the function has been called
 * is maintained, this is used to exclude filenames.
 */
void
process_executable(char *pathname, int (*func)(char *, char *, DIR *, int))
{
	struct sobj *get_share_obj(char *, struct libpath *, int);
	struct sobj *sop;
	struct sobj *psop;

#ifdef DEBUG
	printf("process_executable: pathname = %s\n", pathname);
	fflush(stdout);
#endif /* debug */
	sop = get_share_obj(pathname, &libp_hd, GSO_ADDEXCLD);
#ifdef DEBUG
	printf("process_executable: sop = %x\n", sop);
	fflush(stdout);
#endif /* debug */
	if (verbose) {
		if ((int)sop < 0)  {
			fprintf(stderr,
			    gettext(
			    "cachefspack: unable to get shared objects - %s\n"),
			    pathname);
		}
	}
	if ((int)sop > 0) {
		while (sop->so_next != (struct sobj *)0) {
#ifdef DEBUG
			printf("process_executable: sop->so_name = %s\n",
			    sop->so_name);
			fflush(stdout);
#endif /* DEBUG */
			func_dir_path(sop->so_name, func);

			psop = sop;
			sop = sop->so_next;
			free(psop->so_name);
			free(psop);
		}
	}
}

/*
 * Given the name of an executable, a list of directories to use in the
 * library search and a list of library names to exclude, return all
 * shared object needed by the executable.
 *
 * RETURNS: A pointer to a list of shared objects
 */
struct sobj *
get_share_obj(char *fpath, struct libpath *libpath, int flag)
{
	static int name_cnt = 0;
	static struct sobj *so, *hd_so;
	static int depth = 0;
	static struct libpath *rpath, hd_rpath;
	int found_file = 0;
	int error;
	int fd;
	Elf *elfp;
	Elf32_Ehdr *Ehdr;
	Elf_Scn *scn;
	Elf32_Shdr *shdr;
	Elf32_Dyn *dyn;
	size_t	dynsz;
	char *name;
	void * get_scndata();
	struct sobj *alloc_sobj();
	struct sobj *add_so();
	char pathtmp[MAXPATHLEN];
	ENTRY hitem, *hitemp;
	int fileopn;
	int elfbgn = 0;
	Elf_Kind file_type;
	int buf;

	/*
	 * Open a file and perform necessary operations to find the sections
	 * in an elf format file. If the specified file is not elf format
	 * return an error.
	 */
	depth++;
	if (depth == 1) {
		/*
		 * Find the ending exclude shared object element.
		 */
		rpath = &hd_rpath;
#ifdef DEBUG
		printf("dbg: rpath = %x\n", rpath);
#endif /* DEBUG */
		rpath->lp_path = " ";
		rpath->lp_next = (struct libpath *)0;
		rpath->lp_level = 0;
	}

	fileopn = 0;
	error = ERR_NOERROR;
	fd = open(fpath, O_RDONLY);
	if (fd < 0) {
		error = ERR_NOFILE;
		goto out;
	}
	fileopn = 1;
/* work around */
/*
 * elf_begin() core dumps when passed a file descriptor for a file
 * which does not have read permission, but was opened RDONLY because the
 * user doing the open was root. To avoid this problem, make sure we can
 * read the first byte of the file. If we can't, skip the file. This is a
 * temporary workaround until elf_begin() is fixed.
 */
	if (read(fd, &buf, sizeof (buf)) < 0) {
#ifdef DEBUG
		printf("read failed\n");
		fflush(stdout);
#endif /* DEBUG */
		error = ERR_NOFILE;
		goto out;
	}
	lseek(fd, 0, SEEK_SET);
/* work around end */
	if (elf_version(EV_CURRENT) == EV_NONE) {
		error = ERR_NOELFVER;
		goto out;
	}
	elfbgn = 0;
	if ((elfp = elf_begin(fd,  ELF_C_READ, (Elf *)0)) == NULL) {
		error = ERR_NOELFBEG;
		goto out;
	}
	elfbgn = 1;
	file_type = elf_kind(elfp);
#ifdef DEBUG
	printf("file_type = %x\n", file_type);
	fflush(stdout);
#endif /* DEBUG */

	if (file_type != ELF_K_ELF) {
		goto out;
	}
	if ((Ehdr = elf32_getehdr(elfp)) == NULL) {
		error = ERR_NOELFBEG;
		goto out;
	}
#ifdef DEBUG
	printf("dbg: depth = %d\n", depth);
#endif /* DEBUG */
	/*
	 * Scan all sections of the elf file to locate the dynamic section
	 */
	scn = 0;
	while ((scn = elf_nextscn(elfp, scn)) != 0) {
		if ((shdr = elf32_getshdr(scn)) == NULL) {
			error = ERR_NOELFSHD;
			goto out;
		}
		if (shdr->sh_type != SHT_DYNAMIC) {
			continue;
		}
		/*
		 * The first pass of the dynamic section locates all
		 * directories specified by "ld -R..". A stack is created
		 * for the search, this allows shared libraries, dependant
		 * on other shared libraries, built with "ld -R" to work
		 * properly.
		 *
		 */
		if ((dyn = (Elf32_Dyn *)get_scndata(scn, &dynsz)) == 0) {
			error = ERR_NOELFSDT;
			goto out;
		}
		while (dyn->d_tag != DT_NULL) {
			if (dyn->d_tag == DT_RPATH) {
				name = (char *)elf_strptr(elfp,
				    (size_t)shdr->sh_link, dyn->d_un.d_ptr);
#ifdef DEBUG
				printf("DT_RPATH: name = %s\n", name);
#endif /* DEBUG */
				rpath = stk_libpath(rpath, name, depth);
			}
			dyn++;
		}
		/*
		 * Find all needed shared objects. Do this recursively
		 * so libraries dependant on other libraries are found.
		 * Also, try a list of libraries to exclude. Since
		 * this routine is used by cachefspack, it is only neccessary
		 * to pack a library once. For example, libc is used by lots
		 * of commands, we need not return its name to cachefspack
		 * except the first time we find it.
		 */
		if ((dyn = (Elf32_Dyn *)get_scndata(scn, &dynsz)) == 0) {
			error = ERR_NOELFSDT;
			goto out;
		}
		for (; dyn->d_tag != DT_NULL; dyn++) {
			if (dyn->d_tag == DT_NEEDED) {
				name = (char *)elf_strptr(elfp,
				    (size_t)shdr->sh_link, dyn->d_un.d_ptr);
				if (name != 0) {
#ifdef DEBUG
					printf("chk: %s\n", name);
					fflush(stdout);
#endif /* DEBUG */
					found_file = libsrch(name, libpath,
					    pathtmp);
#ifdef DEBUG
					printf("dbg:  1 found_file = %d\n",
					    found_file);
					fflush(stdout);
#endif /* DEBUG */
					if (!found_file) {
						found_file = libsrch(name,
						    rpath, pathtmp);
					}
#ifdef DEBUG
					printf("dbg:  2 found_file = %d\n",
					    found_file);
					fflush(stdout);
#endif /* DEBUG */
					if (!found_file) {
						continue;
					}
					if (name_cnt == 0) {
						so = alloc_sobj();
						hd_so = so;
					}
					/*
					 * See if file already in list
					 */
					hitem.key = mstrdup(pathtmp);
					hitem.data = 0;
					hitemp = hsearch(hitem, FIND);
					if (hitemp != NULL) {
#ifdef DEBUG
						printf("found so: %s\n",
						    pathtmp);
						printf("hitemp.key = %s\n",
						    hitemp->key);
#endif /* DEBUG */
						continue;
					}
#ifdef DEBUG
					printf("do : %s\n", pathtmp);
					fflush(stdout);
#endif /* DEBUG */
					name_cnt++;
					so = add_so(so, pathtmp);
					if (flag & GSO_ADDEXCLD) {
#ifdef DEBUG
						printf("adding so: %s\n",
						    pathtmp);
#endif /* DEBUG */
						hitem.key = mstrdup(pathtmp);
						hitem.data = 0;
						if (hsearch(hitem, ENTER) ==
						    NULL) {
							error = ERR_HASHFULL;
							goto out;
						}
					}
					get_share_obj(pathtmp, libpath, flag);
				} else {
					if (name_cnt > 0) {
						goto out;
					} else {
						error = ERR_NOELFNAM;
						goto out;
					}
				}
			}
		}
	}

out:
#ifdef DEBUG
	printf("error = %x\n", error);
	fflush(stdout);
#endif /* DEBUG */
	depth--;
#ifdef DEBUG
	printf("ret: depth = %d\n", depth);
	fflush(stdout);
#endif /* DEBUG */
	if (fileopn) {
		close(fd);
		if (elfbgn) {
			if ((error != ERR_NOFILE) && (error != ERR_NOELFVER)) {
				elf_end(elfp);
			}
		}
	}
	if (name_cnt == 0) {
		return ((struct sobj *)ERR_NOERROR);
	}
	while (rpath->lp_level > depth) {
#ifdef DEBUG
		printf("ret: rpath->lp_level = %d\n", rpath->lp_level);
		fflush(stdout);
#endif /* DEBUG */
		rpath = pop_libpath(rpath);
	}
	if (depth == 0) {
		name_cnt = 0;
	}
	if (error == ERR_NOERROR) {
		return (hd_so);
	} else {
		return ((struct sobj *)error);
	}
}


/*
 * Get the section descriptor and set the size of the
 * data returned.  Data is byte-order converted.
 */

void *
get_scndata(fd_scn, size)
Elf_Scn *fd_scn;
size_t    *size;
{
	Elf_Data *p_data;

	p_data = 0;
	if ((p_data = elf_getdata(fd_scn, p_data)) == 0 ||
		p_data->d_size == 0)
	{
		return (NULL);
	}

	*size = p_data->d_size;
	return (p_data->d_buf);
}

/*
 * Allocate a shared object structure
 *
 * RETURNS: A pointer to the allocated structure
 */
struct sobj *
alloc_sobj()
{
	struct sobj *so;
	so = (struct sobj *)mmalloc(sizeof (struct sobj));
	so->so_name = " ";
	so->so_next = (struct sobj *)0;
	return (so);
}


/*
 * Add an object to a shared object list
 *
 * RETURNS: The tail of the shared object list
 */
struct sobj *
add_so(struct sobj *so, char *path)
{
	if (so == (struct sobj *)0) {
		so = alloc_sobj();
	}
	so->so_name = mstrdup(path);
	so->so_next = alloc_sobj();
	so = so->so_next;
	return (so);
}

/*
 * Determine if name concatenated with a library directory path yields
 * a file name that exists.
 *
 * RETURNS: True(1) or False(0)
 *	    if true - fullpath arg contains a pointer to the full path name
 *			of the file
 */
int
libsrch(char *name, struct libpath *libpath, char *fullpath)
{
	struct stat64 statbuf;
	struct libpath *lp;

#ifdef DEBUG
	printf("libsrch: libpath = %x\n", libpath);
	fflush(stdout);
#endif /* DEBUG */
	lp = libpath;
	if (lp == NULL) {
		return (0);
	}
#ifdef DEBUG
	printf("libsrch: 1 lp->lp_next = %x\n", lp->lp_next);
	fflush(stdout);
#endif /* DEBUG */
	while (lp->lp_next != (struct libpath *)0) {
		strcpy(fullpath, lp->lp_path);
		strcat(fullpath, "/");
		strcat(fullpath, name);
		lp = lp->lp_next;
#ifdef DEBUG
		printf("libsrch: 2 lp->lp_next = %x\n", lp->lp_next);
		fflush(stdout);
#endif /* DEBUG */
		/*
		 * stat - if file break
		 */
		if (stat64(fullpath, &statbuf)
		    == 0) {
#ifdef DEBUG
			printf("libsrch: found - %s\n", fullpath);
			fflush(stdout);
#endif /* DEBUG */
			return (1);
		}
	}
#ifdef DEBUG
	printf("libsrch: NOT found - %s\n", name);
	fflush(stdout);
#endif /* DEBUG */
	return (0);
}

/*
 * Add path to the libpath list(add at the tail of the list).
 *
 * RETURNS: The new tail of the list
 */
struct libpath *
add_libpath(struct libpath *lp, char *path, int level)
{
	char *s;

	lp->lp_level = level;
	s = mstrdup(path);
	if (s != (char *)0) {
		lp->lp_path = s;
	}
	lp->lp_next = (struct libpath *)mmalloc(sizeof (struct libpath));
	lp = lp->lp_next;
	lp->lp_next = (struct libpath *)0;
	lp->lp_level = 0;
	lp->lp_path = " ";
	return (lp);
}

/*
 * Add directory/directories in name to libpath stack(as head of the stack)
 * at the level specified.
 *
 * RETURNS: the new head of the stack
 */
struct libpath *
stk_libpath(struct libpath *hd, char *name, int level)
{
	struct libpath *lp, *prev_lp;
	char *s, *t;
	char *tok;
	char *freeit;

#ifdef DEBUG
	printf("stk_libpath: name = %s\n", name);
	fflush(stdout);
#endif /* DEBUG */
	s = mstrdup(name);
	freeit = s;
	prev_lp = hd;
	while (1) {
		tok = strtok(s, ":");
		if (tok == (char *)NULL)
		    break;
		s = (char *)0;
		lp = (struct libpath *)mmalloc(sizeof (struct libpath));
		lp->lp_level = level;
		t = mstrdup(tok);
		lp->lp_path = t;
		lp->lp_next = prev_lp;
		prev_lp = lp;
	}
#ifdef DEBUG
	printf("stk_libpath: lp = %x\n", lp);
	fflush(stdout);
#endif /* DEBUG */
	free(freeit);
	return (lp);
}

/*
 * Free up a libpath stack entry.
 *
 * RETURNS: the new head of the stack
 */
struct libpath *
pop_libpath(struct libpath *lp)
{
	struct libpath *tlp;

	tlp = lp;
	lp = lp->lp_next;
	free(tlp->lp_path);
	free(tlp);
	return (lp);
}

/*
 * Crack the LD_LIBRARY_PATH environment variable. Make a list of libraries
 * to search.
 */
void
get_libsrch_path(struct libpath *libhd)
{
	char *s;
	char *tok = (char *) 1;
	struct libpath *lp;

	lp = libhd;
	s = getenv("LD_LIBRARY_PATH");
	if (s != (char *)NULL) {
		while (1) {
			tok = strtok(s, ":");
			if (tok == (char *) NULL)
				break;
			s = (char *) 0;
			lp = add_libpath(lp, tok, 0);
		}
	}
	add_libpath(lp, "/usr/lib", 0);
}


#ifdef DEBUG
prt_sop_lst(struct sobj *sop, char * str)
{
	printf("\n\n\n%s - sop = %x\n", str, sop);
	fflush(stdout);
	if ((int)sop < 0)  {
		fprintf(stderr, "get_share_obj: failed\n");
		exit(1);
	}

	if ((int)sop > 0) {
		while (sop->so_next != (struct sobj *) 0) {
			printf("sop->so_name = %s\n", sop->so_name);
			sop = sop->so_next;
		}
	}
}
#endif /* DEBUG */