view usr/src/uts/common/krtld/kobj.c @ 4159:61358aef9ab3

6532562 unix needs to be in sync with itself
author josephb
date Thu, 03 May 2007 14:25:49 -0700
parents f6891a60bd72
children d92617caa6c6
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 (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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * Kernel's linker/loader
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/kmem.h>
#include <sys/reboot.h>
#include <sys/bootconf.h>
#include <sys/debug.h>
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/vnode.h>
#include <sys/user.h>
#include <sys/mman.h>
#include <vm/as.h>
#include <vm/seg_kp.h>
#include <vm/seg_kmem.h>
#include <sys/elf.h>
#include <sys/elf_notes.h>
#include <sys/vmsystm.h>
#include <sys/kdi.h>
#include <sys/atomic.h>
#include <sys/kmdb.h>

#include <sys/link.h>
#include <sys/kobj.h>
#include <sys/ksyms.h>
#include <sys/disp.h>
#include <sys/modctl.h>
#include <sys/varargs.h>
#include <sys/kstat.h>
#include <sys/kobj_impl.h>
#include <sys/callb.h>
#include <sys/cmn_err.h>
#include <sys/tnf_probe.h>

#include <reloc.h>
#include <kobj_kdi.h>
#include <sys/sha1.h>
#include <sys/crypto/elfsign.h>

#if !defined(__sparc)
#include <sys/bootvfs.h>
#endif

/*
 * do_symbols() error codes
 */
#define	DOSYM_UNDEF		-1	/* undefined symbol */
#define	DOSYM_UNSAFE		-2	/* MT-unsafe driver symbol */

static void synthetic_bootaux(char *, val_t *);
static struct module *load_exec(val_t *, char *);
static void load_linker(val_t *);
static struct modctl *add_primary(const char *filename, int);
static int bind_primary(val_t *, int);
static int load_primary(struct module *, int);
static int load_kmdb(val_t *);
static int get_progbits(struct module *, struct _buf *);
static int get_syms(struct module *, struct _buf *);
static int get_ctf(struct module *, struct _buf *);
static void get_signature(struct module *, struct _buf *);
static int do_common(struct module *);
static void add_dependent(struct module *, struct module *);
static int do_dependents(struct modctl *, char *, size_t);
static int do_symbols(struct module *, Elf64_Addr);
static void module_assign(struct modctl *, struct module *);
static void free_module_data(struct module *);
static char *depends_on(struct module *);
static char *getmodpath(const char *);
static char *basename(char *);
static void attr_val(val_t *);
static char *find_libmacro(char *);
static char *expand_libmacro(char *, char *, char *);
static int read_bootflags(void);
static int kobj_boot_open(char *, int);
static int kobj_boot_close(int);
static int kobj_boot_seek(int, off_t, off_t);
static int kobj_boot_read(int, caddr_t, size_t);
static int kobj_boot_fstat(int, struct bootstat *);

static Sym *lookup_one(struct module *, const char *);
static void sym_insert(struct module *, char *, symid_t);
static Sym *sym_lookup(struct module *, Sym *);

/*PRINTFLIKE2*/
static void kprintf(void *, const char *, ...)  __KPRINTFLIKE(2);

static struct kobjopen_tctl *kobjopen_alloc(char *filename);
static void kobjopen_free(struct kobjopen_tctl *ltp);
static void kobjopen_thread(struct kobjopen_tctl *ltp);

extern int kcopy(const void *, void *, size_t);
extern int elf_mach_ok(Ehdr *);
extern int alloc_gottable(struct module *, caddr_t *, caddr_t *);

static void tnf_unsplice_probes(unsigned int, struct modctl *);

extern int modrootloaded;
extern int swaploaded;
extern int bop_io_quiesced;
extern int last_module_id;

#ifdef KOBJ_DEBUG
/*
 * Values that can be or'd in to kobj_debug and their effects:
 *
 *	D_DEBUG		- misc. debugging information.
 *	D_SYMBOLS	- list symbols and their values as they are entered
 *			  into the hash table
 *	D_RELOCATIONS	- display relocation processing information
 *	D_LOADING	- display information about each module as it
 *			  is loaded.
 */
int kobj_debug = 0;

#define	KOBJ_MARK(s)	if (kobj_debug & D_DEBUG)	\
	(_kobj_printf(ops, "%d", __LINE__), _kobj_printf(ops, ": %s\n", s))
#else
#define	KOBJ_MARK(s)	/* discard */
#endif

#define	MODPATH_PROPNAME	"module-path"

#ifdef MODDIR_SUFFIX
static char slash_moddir_suffix_slash[] = MODDIR_SUFFIX "/";
#else
#define	slash_moddir_suffix_slash	""
#endif

#define	_moddebug	get_weakish_int(&moddebug)
#define	_modrootloaded	get_weakish_int(&modrootloaded)
#define	_swaploaded	get_weakish_int(&swaploaded)
#define	_ioquiesced	get_weakish_int(&bop_io_quiesced)

#define	mod(X)		(struct module *)((X)->modl_modp->mod_mp)

void	*romp;		/* rom vector (opaque to us) */
struct bootops *ops;	/* bootops vector */
void *dbvec;		/* debug vector */

/*
 * kobjopen thread control structure
 */
struct kobjopen_tctl {
	ksema_t		sema;
	char		*name;		/* name of file */
	struct vnode	*vp;		/* vnode return from vn_open() */
	int		Errno;		/* error return from vnopen    */
};

/*
 * Structure for defining dynamically expandable library macros
 */

struct lib_macro_info {
	char	*lmi_list;		/* ptr to list of possible choices */
	char	*lmi_macroname;		/* pointer to macro name */
	ushort_t lmi_ba_index;		/* index into bootaux vector */
	ushort_t lmi_macrolen;		/* macro length */
} libmacros[] = {
	{ NULL, "CPU", BA_CPU, 0 },
	{ NULL, "MMU", BA_MMU, 0 }
};

#define	NLIBMACROS	sizeof (libmacros) / sizeof (struct lib_macro_info)

char *boot_cpu_compatible_list;			/* make $CPU available */

#ifdef	MPSAS
void	sas_prisyms(struct modctl_list *);
void	sas_syms(struct module *);
#endif

char *kobj_module_path;				/* module search path */
vmem_t	*text_arena;				/* module text arena */
static vmem_t *data_arena;			/* module data & bss arena */
static vmem_t *ctf_arena;			/* CTF debug data arena */
static struct modctl *kobj_modules = NULL;	/* modules loaded */
int kobj_mmu_pagesize;				/* system pagesize */
static int lg_pagesize;				/* "large" pagesize */
static int kobj_last_module_id = 0;		/* id assignment */
static kmutex_t kobj_lock;			/* protects mach memory list */

/*
 * The following functions have been implemented by the kernel.
 * However, many 3rd party drivers provide their own implementations
 * of these functions.  When such drivers are loaded, messages
 * indicateing that these symbols have been mulply defined will be
 * emitted to the console.  To avoid alarming customers for no good
 * reason, we simply suppress such warnings for the following set of
 * functions.
 */
static char *suppress_sym_list[] =
{
	"strstr",
	"strncat",
	"strlcat",
	"strlcpy",
	"strspn",
	"memcpy",
	"memset",
	"memmove",
	"memcmp",
	"memchr",
	"__udivdi3",
	"__divdi3",
	"__umoddi3",
	"__moddi3",
	NULL		/* This entry must exist */
};

/* indexed by KOBJ_NOTIFY_* */
static kobj_notify_list_t *kobj_notifiers[KOBJ_NOTIFY_MAX + 1];

/*
 * TNF probe management globals
 */
tnf_probe_control_t	*__tnf_probe_list_head = NULL;
tnf_tag_data_t		*__tnf_tag_list_head = NULL;
int			tnf_changed_probe_list = 0;

/*
 * Prefix for statically defined tracing (SDT) DTrace probes.
 */
const char		*sdt_prefix = "__dtrace_probe_";

#if defined(__sparc)
/*
 * Some PROMs return SUNW,UltraSPARC when they actually have
 * SUNW,UltraSPARC-II cpus. SInce we're now filtering out all
 * SUNW,UltraSPARC systems during the boot phase, we can safely
 * point the auxv CPU value at SUNW,UltraSPARC-II. This is what
 * we point it at.
 */
const char		*ultra_2 = "SUNW,UltraSPARC-II";
#endif

/*
 * Beginning and end of the kernel's dynamic text/data segments.
 */
static caddr_t _text;
static caddr_t _etext;
static caddr_t _data;

/*
 * XXX Hmm. The sparc linker fails to define this symbol.
 */
#if !defined(__sparc)
extern
#endif
caddr_t _edata;

static Addr dynseg = 0;	/* load address of "dynamic" segment */

int standalone = 1;			/* an unwholey kernel? */
int use_iflush;				/* iflush after relocations */

/*
 * _kobj_printf()
 *
 * Common printf function pointer. Can handle only one conversion
 * specification in the format string. Some of the functions invoked
 * through this function pointer cannot handle more that one conversion
 * specification in the format string.
 */
void (*_kobj_printf)(void *, const char *, ...);	/* printf routine */

static kobj_stat_t kobj_stat;

#define	MINALIGN	8	/* at least a double-word */

int
get_weakish_int(int *ip)
{
	if (standalone)
		return (0);
	return (ip == NULL ? 0 : *ip);
}

static void *
get_weakish_pointer(void **ptrp)
{
	if (standalone)
		return (0);
	return (ptrp == NULL ? 0 : *ptrp);
}

/*
 * XXX fix dependencies on "kernel"; this should work
 * for other standalone binaries as well.
 *
 * XXX Fix hashing code to use one pointer to
 * hash entries.
 *	|----------|
 *	| nbuckets |
 *	|----------|
 *	| nchains  |
 *	|----------|
 *	| bucket[] |
 *	|----------|
 *	| chain[]  |
 *	|----------|
 */

/*
 * Load, bind and relocate all modules that
 * form the primary kernel. At this point, our
 * externals have not been relocated.
 */
void
kobj_init(
	void *romvec,
	void *dvec,
	struct bootops *bootvec,
	val_t *bootaux)
{
	struct module *mp;
	struct modctl *modp;
	Addr entry;
	char filename[MAXPATHLEN];

	/*
	 * Save these to pass on to
	 * the booted standalone.
	 */
	romp = romvec;
	dbvec = dvec;

	ops = bootvec;
#if defined(__i386) || defined(__amd64)
	_kobj_printf = (void (*)(void *, const char *, ...))ops->bsys_printf;
#else
	_kobj_printf = (void (*)(void *, const char *, ...))bop_putsarg;
#endif
	KOBJ_MARK("Entered kobj_init()");

#if defined(__sparc)
	/* XXXQ should suppress this test on sun4v */
	if (bootaux[BA_CPU].ba_ptr) {
		if (strcmp("SUNW,UltraSPARC", bootaux[BA_CPU].ba_ptr) == 0) {
			bootaux[BA_CPU].ba_ptr = (void *) ultra_2;
		}
	}
#endif

	/*
	 * Check bootops version.
	 */
	if (BOP_GETVERSION(ops) != BO_VERSION) {
		_kobj_printf(ops, "Warning: Using boot version %d, ",
		    BOP_GETVERSION(ops));
		_kobj_printf(ops, "expected %d\n", BO_VERSION);
	}
#ifdef KOBJ_DEBUG
	else if (kobj_debug & D_DEBUG) {
		/*
		 * Say -something- so we know we got this far ..
		 */
		_kobj_printf(ops, "krtld: Using boot version %d.\n",
		    BOP_GETVERSION(ops));
	}
#endif

	(void) BOP_GETPROP(ops, "whoami", filename);

	/*
	 * We don't support standalone debuggers anymore.  The use of kadb
	 * will interfere with the later use of kmdb.  Let the user mend
	 * their ways now.  Users will reach this message if they still
	 * have the kadb binary on their system (perhaps they used an old
	 * bfu, or maybe they intentionally copied it there) and have
	 * specified its use in a way that eluded our checking in the boot
	 * program.
	 */
	if (dvec != NULL) {
		_kobj_printf(ops, "\nWARNING: Standalone debuggers such as "
		    "kadb are no longer supported\n\n");
		goto fail;
	}

#ifndef __sparc
	{
		/* on x86, we always boot with a ramdisk */
		extern int kobj_boot_mountroot(void);
		(void) kobj_boot_mountroot();

		/*
		 * Now that the ramdisk is mounted, finish boot property
		 * initialization.
		 */
		boot_prop_finish();
	}
#endif

#if !defined(_UNIX_KRTLD)
	/*
	 * If 'unix' is linked together with 'krtld' into one executable,
	 * the early boot code does -not- hand us any of the dynamic metadata
	 * about the executable. In particular, it does not read in, map or
	 * otherwise look at the program headers. We fake all that up now.
	 *
	 * We do this early as DTrace static probes and tnf probes both call
	 * undefined references.  We have to process those relocations before
	 * calling any of them.
	 */
	if (bootaux[BA_PHDR].ba_ptr == NULL)
		synthetic_bootaux(filename, bootaux);
#endif

	/*
	 * Save the interesting attribute-values
	 * (scanned by kobj_boot).
	 */
	attr_val(bootaux);

	/*
	 * Set the module search path.
	 */
	kobj_module_path = getmodpath(filename);

	boot_cpu_compatible_list = find_libmacro("CPU");

	/*
	 * These two modules have actually been
	 * loaded by boot, but we finish the job
	 * by introducing them into the world of
	 * loadable modules.
	 */

	mp = load_exec(bootaux, filename);
	load_linker(bootaux);

	/*
	 * Load all the primary dependent modules.
	 */
	if (load_primary(mp, KOBJ_LM_PRIMARY) == -1)
		goto fail;

	/*
	 * Glue it together.
	 */
	if (bind_primary(bootaux, KOBJ_LM_PRIMARY) == -1)
		goto fail;

	entry = bootaux[BA_ENTRY].ba_val;

#ifdef	__sparc
	/*
	 * On sparcv9, boot scratch memory is running out.
	 * Free the temporary allocations here to allow boot
	 * to continue.
	 */
	kobj_tmp_free();
#endif

	/*
	 * Get the boot flags
	 */
	bootflags(ops);

	if (boothowto & RB_VERBOSE)
		kobj_lm_dump(KOBJ_LM_PRIMARY);

	kobj_kdi_init();

	if (boothowto & RB_KMDB) {
		if (load_kmdb(bootaux) < 0)
			goto fail;
	}

	/*
	 * Post setup.
	 */
#ifdef	MPSAS
	sas_prisyms(kobj_lm_lookup(KOBJ_LM_PRIMARY));
#endif
	s_text = _text;
	e_text = _etext;
	s_data = _data;
	e_data = _edata;

	kobj_sync_instruction_memory(s_text, e_text - s_text);

#ifdef	KOBJ_DEBUG
	if (kobj_debug & D_DEBUG)
		_kobj_printf(ops,
		    "krtld: transferring control to: 0x%p\n", entry);
#endif

	/*
	 * Make sure the mod system knows about the modules already loaded.
	 */
	last_module_id = kobj_last_module_id;
	bcopy(kobj_modules, &modules, sizeof (modules));
	modp = &modules;
	do {
		if (modp->mod_next == kobj_modules)
			modp->mod_next = &modules;
		if (modp->mod_prev == kobj_modules)
			modp->mod_prev = &modules;
	} while ((modp = modp->mod_next) != &modules);

	standalone = 0;

#ifdef	__sparc
	/*
	 * On sparcv9, boot scratch memory is running out.
	 * Free the temporary allocations here to allow boot
	 * to continue.
	 */
	kobj_tmp_free();
#endif

	_kobj_printf = kprintf;
	exitto((caddr_t)entry);
fail:

	_kobj_printf(ops, "krtld: error during initial load/link phase\n");

#if !defined(_UNIX_KRTLD)
	_kobj_printf(ops, "\n");
	_kobj_printf(ops, "krtld could neither locate nor resolve symbols"
	    " for:\n");
	_kobj_printf(ops, "    %s\n", filename);
	_kobj_printf(ops, "in the boot archive. Please verify that this"
	    " file\n");
	_kobj_printf(ops, "matches what is found in the boot archive.\n");
	_kobj_printf(ops, "You may need to boot using the Solaris failsafe to"
	    " fix this.\n");
	bop_panic("Unable to boot");
#endif
}

#if !defined(_UNIX_KRTLD)
/*
 * Synthesize additional metadata that describes the executable.
 *
 * (When the dynamic executable has an interpreter, the boot program
 * does all this for us.  Where we don't have an interpreter, (or a
 * even a boot program, perhaps) we have to do this for ourselves.)
 */
static void
synthetic_bootaux(char *filename, val_t *bootaux)
{
	Ehdr ehdr;
	caddr_t phdrbase;
	struct _buf *file;
	int i, n;

	/*
	 * Elf header
	 */
	KOBJ_MARK("synthetic_bootaux()");
	KOBJ_MARK(filename);
	file = kobj_open_file(filename);
	if (file == (struct _buf *)-1) {
		_kobj_printf(ops, "krtld: failed to open '%s'\n", filename);
		return;
	}
	KOBJ_MARK("reading program headers");
	if (kobj_read_file(file, (char *)&ehdr, sizeof (ehdr), 0) < 0) {
		_kobj_printf(ops, "krtld: %s: failed to read ehder\n",
		    filename);
		return;
	}

	/*
	 * Program headers
	 */
	bootaux[BA_PHNUM].ba_val = ehdr.e_phnum;
	bootaux[BA_PHENT].ba_val = ehdr.e_phentsize;
	n = ehdr.e_phentsize * ehdr.e_phnum;

	phdrbase = kobj_alloc(n, KM_WAIT | KM_TMP);

	if (kobj_read_file(file, phdrbase, n, ehdr.e_phoff) < 0) {
		_kobj_printf(ops, "krtld: %s: failed to read phdrs\n",
		    filename);
		return;
	}
	bootaux[BA_PHDR].ba_ptr = phdrbase;
	kobj_close_file(file);
	KOBJ_MARK("closed file");

	/*
	 * Find the dynamic section address
	 */
	for (i = 0; i < ehdr.e_phnum; i++) {
		Phdr *phdr = (Phdr *)(phdrbase + ehdr.e_phentsize * i);

		if (phdr->p_type == PT_DYNAMIC) {
			bootaux[BA_DYNAMIC].ba_ptr = (void *)phdr->p_vaddr;
			break;
		}
	}
	KOBJ_MARK("synthetic_bootaux() done");
}
#endif

/*
 * Set up any global information derived
 * from attribute/values in the boot or
 * aux vector.
 */
static void
attr_val(val_t *bootaux)
{
	Phdr *phdr;
	int phnum, phsize;
	int i;

	KOBJ_MARK("attr_val()");
	kobj_mmu_pagesize = bootaux[BA_PAGESZ].ba_val;
	lg_pagesize = bootaux[BA_LPAGESZ].ba_val;
	use_iflush = bootaux[BA_IFLUSH].ba_val;

	phdr = (Phdr *)bootaux[BA_PHDR].ba_ptr;
	phnum = bootaux[BA_PHNUM].ba_val;
	phsize = bootaux[BA_PHENT].ba_val;
	for (i = 0; i < phnum; i++) {
		phdr = (Phdr *)(bootaux[BA_PHDR].ba_val + i * phsize);

		if (phdr->p_type != PT_LOAD)
			continue;
		/*
		 * Bounds of the various segments.
		 */
		if (!(phdr->p_flags & PF_X)) {
#if defined(_UNIX_KRTLD)
			dynseg = phdr->p_vaddr;
#else
			ASSERT(phdr->p_vaddr == 0);
#endif
		} else {
			if (phdr->p_flags & PF_W) {
				_data = (caddr_t)phdr->p_vaddr;
				_edata = _data + phdr->p_memsz;
			} else {
				_text = (caddr_t)phdr->p_vaddr;
				_etext = _text + phdr->p_memsz;
			}
		}
	}

	/* To do the kobj_alloc, _edata needs to be set. */
	for (i = 0; i < NLIBMACROS; i++) {
		if (bootaux[libmacros[i].lmi_ba_index].ba_ptr != NULL) {
			libmacros[i].lmi_list = kobj_alloc(
			    strlen(bootaux[libmacros[i].lmi_ba_index].ba_ptr) +
			    1, KM_WAIT);
			(void) strcpy(libmacros[i].lmi_list,
			    bootaux[libmacros[i].lmi_ba_index].ba_ptr);
		}
		libmacros[i].lmi_macrolen = strlen(libmacros[i].lmi_macroname);
	}
}

/*
 * Set up the booted executable.
 */
static struct module *
load_exec(val_t *bootaux, char *filename)
{
	struct modctl *cp;
	struct module *mp;
	Dyn *dyn;
	Sym *sp;
	int i, lsize, osize, nsize, allocsize;
	char *libname, *tmp;

	/*
	 * Set the module search path.
	 */
	kobj_module_path = getmodpath(filename);

#ifdef KOBJ_DEBUG
	if (kobj_debug & D_DEBUG)
		_kobj_printf(ops, "module path '%s'\n", kobj_module_path);
#endif

	KOBJ_MARK("add_primary");
	cp = add_primary(filename, KOBJ_LM_PRIMARY);

	KOBJ_MARK("struct module");
	mp = kobj_zalloc(sizeof (struct module), KM_WAIT);
	cp->mod_mp = mp;

	/*
	 * We don't have the following information
	 * since this module is an executable and not
	 * a relocatable .o.
	 */
	mp->symtbl_section = 0;
	mp->shdrs = NULL;
	mp->strhdr = NULL;

	/*
	 * Since this module is the only exception,
	 * we cons up some section headers.
	 */
	KOBJ_MARK("symhdr");
	mp->symhdr = kobj_zalloc(sizeof (Shdr), KM_WAIT);

	KOBJ_MARK("strhdr");
	mp->strhdr = kobj_zalloc(sizeof (Shdr), KM_WAIT);

	mp->symhdr->sh_type = SHT_SYMTAB;
	mp->strhdr->sh_type = SHT_STRTAB;
	/*
	 * Scan the dynamic structure.
	 */
	for (dyn = (Dyn *) bootaux[BA_DYNAMIC].ba_ptr;
	    dyn->d_tag != DT_NULL; dyn++) {
		switch (dyn->d_tag) {
		case DT_SYMTAB:
			dyn->d_un.d_ptr += dynseg;
			mp->symspace = mp->symtbl = (char *)dyn->d_un.d_ptr;
			mp->symhdr->sh_addr = dyn->d_un.d_ptr;
			break;
		case DT_HASH:
			dyn->d_un.d_ptr += dynseg;
			mp->nsyms = *((uint_t *)dyn->d_un.d_ptr + 1);
			mp->hashsize = *(uint_t *)dyn->d_un.d_ptr;
			break;
		case DT_STRTAB:
			dyn->d_un.d_ptr += dynseg;
			mp->strings = (char *)dyn->d_un.d_ptr;
			mp->strhdr->sh_addr = dyn->d_un.d_ptr;
			break;
		case DT_STRSZ:
			mp->strhdr->sh_size = dyn->d_un.d_val;
			break;
		case DT_SYMENT:
			mp->symhdr->sh_entsize = dyn->d_un.d_val;
			break;
		}
	}

	/*
	 * Collapse any DT_NEEDED entries into one string.
	 */
	nsize = osize = 0;
	allocsize = MAXPATHLEN;

	KOBJ_MARK("depends_on");
	mp->depends_on = kobj_alloc(allocsize, KM_WAIT);

	for (dyn = (Dyn *) bootaux[BA_DYNAMIC].ba_ptr;
	    dyn->d_tag != DT_NULL; dyn++)
		if (dyn->d_tag == DT_NEEDED) {
			char *_lib;

			libname = mp->strings + dyn->d_un.d_val;
			if (strchr(libname, '$') != NULL) {
				if ((_lib = expand_libmacro(libname,
				    filename, filename)) != NULL)
					libname = _lib;
				else
					_kobj_printf(ops, "krtld: "
					    "load_exec: fail to "
					    "expand %s\n", libname);
			}
			lsize = strlen(libname);
			nsize += lsize;
			if (nsize + 1 > allocsize) {
				KOBJ_MARK("grow depends_on");
				tmp = kobj_alloc(allocsize + MAXPATHLEN,
				    KM_WAIT);
				bcopy(mp->depends_on, tmp, osize);
				kobj_free(mp->depends_on, allocsize);
				mp->depends_on = tmp;
				allocsize += MAXPATHLEN;
			}
			bcopy(libname, mp->depends_on + osize, lsize);
			*(mp->depends_on + nsize) = ' '; /* seperate */
			nsize++;
			osize = nsize;
		}
	if (nsize) {
		mp->depends_on[nsize - 1] = '\0'; /* terminate the string */
		/*
		 * alloc with exact size and copy whatever it got over
		 */
		KOBJ_MARK("realloc depends_on");
		tmp = kobj_alloc(nsize, KM_WAIT);
		bcopy(mp->depends_on, tmp, nsize);
		kobj_free(mp->depends_on, allocsize);
		mp->depends_on = tmp;
	} else {
		kobj_free(mp->depends_on, allocsize);
		mp->depends_on = NULL;
	}

	mp->flags = KOBJ_EXEC|KOBJ_PRIM;	/* NOT a relocatable .o */
	mp->symhdr->sh_size = mp->nsyms * mp->symhdr->sh_entsize;
	/*
	 * We allocate our own table since we don't
	 * hash undefined references.
	 */
	KOBJ_MARK("chains");
	mp->chains = kobj_zalloc(mp->nsyms * sizeof (symid_t), KM_WAIT);
	KOBJ_MARK("buckets");
	mp->buckets = kobj_zalloc(mp->hashsize * sizeof (symid_t), KM_WAIT);

	mp->text = _text;
	mp->data = _data;

	mp->text_size = _etext - _text;
	mp->data_size = _edata - _data;

	cp->mod_text = mp->text;
	cp->mod_text_size = mp->text_size;

	mp->filename = cp->mod_filename;

#ifdef	KOBJ_DEBUG
	if (kobj_debug & D_LOADING) {
		_kobj_printf(ops, "krtld: file=%s\n", mp->filename);
		_kobj_printf(ops, "\ttext: 0x%p", mp->text);
		_kobj_printf(ops, " size: 0x%x\n", mp->text_size);
		_kobj_printf(ops, "\tdata: 0x%p", mp->data);
		_kobj_printf(ops, " dsize: 0x%x\n", mp->data_size);
	}
#endif /* KOBJ_DEBUG */

	/*
	 * Insert symbols into the hash table.
	 */
	for (i = 0; i < mp->nsyms; i++) {
		sp = (Sym *)(mp->symtbl + i * mp->symhdr->sh_entsize);

		if (sp->st_name == 0 || sp->st_shndx == SHN_UNDEF)
			continue;
#ifdef	__sparc
		/*
		 * Register symbols are ignored in the kernel
		 */
		if (ELF_ST_TYPE(sp->st_info) == STT_SPARC_REGISTER)
			continue;
#endif	/* __sparc */

		sym_insert(mp, mp->strings + sp->st_name, i);
	}

	KOBJ_MARK("load_exec done");
	return (mp);
}

/*
 * Set up the linker module (if it's compiled in, LDNAME is NULL)
 */
static void
load_linker(val_t *bootaux)
{
	struct module *kmp = (struct module *)kobj_modules->mod_mp;
	struct module *mp;
	struct modctl *cp;
	int i;
	Shdr *shp;
	Sym *sp;
	int shsize;
	char *dlname = (char *)bootaux[BA_LDNAME].ba_ptr;

	/*
	 * On some architectures, krtld is compiled into the kernel.
	 */
	if (dlname == NULL)
		return;

	cp = add_primary(dlname, KOBJ_LM_PRIMARY);

	mp = kobj_zalloc(sizeof (struct module), KM_WAIT);

	cp->mod_mp = mp;
	mp->hdr = *(Ehdr *)bootaux[BA_LDELF].ba_ptr;
	shsize = mp->hdr.e_shentsize * mp->hdr.e_shnum;
	mp->shdrs = kobj_alloc(shsize, KM_WAIT);
	bcopy(bootaux[BA_LDSHDR].ba_ptr, mp->shdrs, shsize);

	for (i = 1; i < (int)mp->hdr.e_shnum; i++) {
		shp = (Shdr *)(mp->shdrs + (i * mp->hdr.e_shentsize));

		if (shp->sh_flags & SHF_ALLOC) {
			if (shp->sh_flags & SHF_WRITE) {
				if (mp->data == NULL)
					mp->data = (char *)shp->sh_addr;
			} else if (mp->text == NULL) {
				mp->text = (char *)shp->sh_addr;
			}
		}
		if (shp->sh_type == SHT_SYMTAB) {
			mp->symtbl_section = i;
			mp->symhdr = shp;
			mp->symspace = mp->symtbl = (char *)shp->sh_addr;
		}
	}
	mp->nsyms = mp->symhdr->sh_size / mp->symhdr->sh_entsize;
	mp->flags = KOBJ_INTERP|KOBJ_PRIM;
	mp->strhdr = (Shdr *)
	    (mp->shdrs + mp->symhdr->sh_link * mp->hdr.e_shentsize);
	mp->strings = (char *)mp->strhdr->sh_addr;
	mp->hashsize = kobj_gethashsize(mp->nsyms);

	mp->symsize = mp->symhdr->sh_size + mp->strhdr->sh_size + sizeof (int) +
	    (mp->hashsize + mp->nsyms) * sizeof (symid_t);

	mp->chains = kobj_zalloc(mp->nsyms * sizeof (symid_t), KM_WAIT);
	mp->buckets = kobj_zalloc(mp->hashsize * sizeof (symid_t), KM_WAIT);

	mp->bss = bootaux[BA_BSS].ba_val;
	mp->bss_align = 0;	/* pre-aligned during allocation */
	mp->bss_size = (uintptr_t)_edata - mp->bss;
	mp->text_size = _etext - mp->text;
	mp->data_size = _edata - mp->data;
	mp->filename = cp->mod_filename;
	cp->mod_text = mp->text;
	cp->mod_text_size = mp->text_size;

	/*
	 * Now that we've figured out where the linker is,
	 * set the limits for the booted object.
	 */
	kmp->text_size = (size_t)(mp->text - kmp->text);
	kmp->data_size = (size_t)(mp->data - kmp->data);
	kobj_modules->mod_text_size = kmp->text_size;

#ifdef	KOBJ_DEBUG
	if (kobj_debug & D_LOADING) {
		_kobj_printf(ops, "krtld: file=%s\n", mp->filename);
		_kobj_printf(ops, "\ttext:0x%p", mp->text);
		_kobj_printf(ops, " size: 0x%x\n", mp->text_size);
		_kobj_printf(ops, "\tdata:0x%p", mp->data);
		_kobj_printf(ops, " dsize: 0x%x\n", mp->data_size);
	}
#endif /* KOBJ_DEBUG */

	/*
	 * Insert the symbols into the hash table.
	 */
	for (i = 0; i < mp->nsyms; i++) {
		sp = (Sym *)(mp->symtbl + i * mp->symhdr->sh_entsize);

		if (sp->st_name == 0 || sp->st_shndx == SHN_UNDEF)
			continue;
		if (ELF_ST_BIND(sp->st_info) == STB_GLOBAL) {
			if (sp->st_shndx == SHN_COMMON)
				sp->st_shndx = SHN_ABS;
		}
		sym_insert(mp, mp->strings + sp->st_name, i);
	}

}

static kobj_notify_list_t **
kobj_notify_lookup(uint_t type)
{
	ASSERT(type != 0 && type < sizeof (kobj_notifiers) /
	    sizeof (kobj_notify_list_t *));

	return (&kobj_notifiers[type]);
}

int
kobj_notify_add(kobj_notify_list_t *knp)
{
	kobj_notify_list_t **knl;

	knl = kobj_notify_lookup(knp->kn_type);

	knp->kn_next = NULL;
	knp->kn_prev = NULL;

	mutex_enter(&kobj_lock);

	if (*knl != NULL) {
		(*knl)->kn_prev = knp;
		knp->kn_next = *knl;
	}
	(*knl) = knp;

	mutex_exit(&kobj_lock);
	return (0);
}

int
kobj_notify_remove(kobj_notify_list_t *knp)
{
	kobj_notify_list_t **knl = kobj_notify_lookup(knp->kn_type);
	kobj_notify_list_t *tknp;

	mutex_enter(&kobj_lock);

	/* LINTED */
	if (tknp = knp->kn_next)
		tknp->kn_prev = knp->kn_prev;

	/* LINTED */
	if (tknp = knp->kn_prev)
		tknp->kn_next = knp->kn_next;
	else
		*knl = knp->kn_next;

	mutex_exit(&kobj_lock);

	return (0);
}

/*
 * Notify all interested callbacks of a specified change in module state.
 */
static void
kobj_notify(int type, struct modctl *modp)
{
	kobj_notify_list_t *knp;

	if (modp->mod_loadflags & MOD_NONOTIFY || standalone)
		return;

	mutex_enter(&kobj_lock);

	for (knp = *(kobj_notify_lookup(type)); knp != NULL; knp = knp->kn_next)
		knp->kn_func(type, modp);

	/*
	 * KDI notification must be last (it has to allow for work done by the
	 * other notification callbacks), so we call it manually.
	 */
	kobj_kdi_mod_notify(type, modp);

	mutex_exit(&kobj_lock);
}

/*
 * Ask boot for the module path.
 */
/*ARGSUSED*/
static char *
getmodpath(const char *filename)
{
	char *path;
	int len;

#if defined(_UNIX_KRTLD)
	/*
	 * The boot program provides the module name when it detects
	 * that the executable has an interpreter, thus we can ask
	 * it directly in this case.
	 */
	if ((len = BOP_GETPROPLEN(ops, MODPATH_PROPNAME)) == -1)
		return (MOD_DEFPATH);

	path = kobj_zalloc(len, KM_WAIT);

	(void) BOP_GETPROP(ops, MODPATH_PROPNAME, path);

	return (*path ? path : MOD_DEFPATH);

#else

	/*
	 * Construct the directory path from the filename.
	 */

	char *p;
	const char isastr[] = "/amd64";
	size_t isalen = strlen(isastr);

	if ((p = strrchr(filename, '/')) == NULL)
		return (MOD_DEFPATH);

	while (p > filename && *(p - 1) == '/')
		p--;	/* remove trailing '/' characters */
	if (p == filename)
		p++;	/* so "/" -is- the modpath in this case */

	/*
	 * Remove optional isa-dependent directory name - the module
	 * subsystem will put this back again (!)
	 */
	len = p - filename;
	if (len > isalen &&
	    strncmp(&filename[len - isalen], isastr, isalen) == 0)
		p -= isalen;

	/*
	 * "/platform/mumblefrotz" + " " + MOD_DEFPATH
	 */
	len += (p - filename) + 1 + strlen(MOD_DEFPATH) + 1;

	path = kobj_zalloc(len, KM_WAIT);
	(void) strncpy(path, filename, p - filename);
	(void) strcat(path, " ");
	return (strcat(path, MOD_DEFPATH));
#endif
}

static struct modctl *
add_primary(const char *filename, int lmid)
{
	struct modctl *cp;

	cp = kobj_zalloc(sizeof (struct modctl), KM_WAIT);

	cp->mod_filename = kobj_alloc(strlen(filename) + 1, KM_WAIT);

	/*
	 * For symbol lookup, we assemble our own
	 * modctl list of the primary modules.
	 */

	(void) strcpy(cp->mod_filename, filename);
	cp->mod_modname = basename(cp->mod_filename);

	/* set values for modinfo assuming that the load will work */
	cp->mod_prim = 1;
	cp->mod_loaded = 1;
	cp->mod_installed = 1;
	cp->mod_loadcnt = 1;
	cp->mod_loadflags = MOD_NOAUTOUNLOAD;

	cp->mod_id = kobj_last_module_id++;

	/*
	 * Link the module in. We'll pass this info on
	 * to the mod squad later.
	 */
	if (kobj_modules == NULL) {
		kobj_modules = cp;
		cp->mod_prev = cp->mod_next = cp;
	} else {
		cp->mod_prev = kobj_modules->mod_prev;
		cp->mod_next = kobj_modules;
		kobj_modules->mod_prev->mod_next = cp;
		kobj_modules->mod_prev = cp;
	}

	kobj_lm_append(lmid, cp);

	return (cp);
}

static int
bind_primary(val_t *bootaux, int lmid)
{
	struct modctl_list *linkmap = kobj_lm_lookup(lmid);
	struct modctl_list *lp;
	struct module *mp;

	/*
	 * Do common symbols.
	 */
	for (lp = linkmap; lp; lp = lp->modl_next) {
		mp = mod(lp);

		/*
		 * Don't do common section relocations for modules that
		 * don't need it.
		 */
		if (mp->flags & (KOBJ_EXEC|KOBJ_INTERP))
			continue;

		if (do_common(mp) < 0)
			return (-1);
	}

	/*
	 * Resolve symbols.
	 */
	for (lp = linkmap; lp; lp = lp->modl_next) {
		mp = mod(lp);

		if (do_symbols(mp, 0) < 0)
			return (-1);
	}

	/*
	 * Do relocations.
	 */
	for (lp = linkmap; lp; lp = lp->modl_next) {
		mp = mod(lp);

		if (mp->flags & KOBJ_EXEC) {
			Dyn *dyn;
			Word relasz = 0, relaent = 0;
			Word shtype;
			char *rela = NULL;

			for (dyn = (Dyn *)bootaux[BA_DYNAMIC].ba_ptr;
			    dyn->d_tag != DT_NULL; dyn++) {
				switch (dyn->d_tag) {
				case DT_RELASZ:
				case DT_RELSZ:
					relasz = dyn->d_un.d_val;
					break;
				case DT_RELAENT:
				case DT_RELENT:
					relaent = dyn->d_un.d_val;
					break;
				case DT_RELA:
					shtype = SHT_RELA;
					rela = (char *)(dyn->d_un.d_ptr +
					    dynseg);
					break;
				case DT_REL:
					shtype = SHT_REL;
					rela = (char *)(dyn->d_un.d_ptr +
					    dynseg);
					break;
				}
			}
			if (relasz == 0 ||
			    relaent == 0 || rela == NULL) {
				_kobj_printf(ops, "krtld: bind_primary(): "
				    "no relocation information found for "
				    "module %s\n", mp->filename);
				return (-1);
			}
#ifdef	KOBJ_DEBUG
			if (kobj_debug & D_RELOCATIONS)
				_kobj_printf(ops, "krtld: relocating: file=%s "
				    "KOBJ_EXEC\n", mp->filename);
#endif
			if (do_relocate(mp, rela, shtype, relasz/relaent,
			    relaent, (Addr)mp->text) < 0)
				return (-1);
		} else {
			if (do_relocations(mp) < 0)
				return (-1);
		}

		kobj_sync_instruction_memory(mp->text, mp->text_size);
	}

	for (lp = linkmap; lp; lp = lp->modl_next) {
		mp = mod(lp);

		/*
		 * We need to re-read the full symbol table for the boot file,
		 * since we couldn't use the full one before.  We also need to
		 * load the CTF sections of both the boot file and the
		 * interpreter (us).
		 */
		if (mp->flags & KOBJ_EXEC) {
			struct _buf *file;
			int n;

			file = kobj_open_file(mp->filename);
			if (file == (struct _buf *)-1)
				return (-1);
			if (kobj_read_file(file, (char *)&mp->hdr,
			    sizeof (mp->hdr), 0) < 0)
				return (-1);
			n = mp->hdr.e_shentsize * mp->hdr.e_shnum;
			mp->shdrs = kobj_alloc(n, KM_WAIT);
			if (kobj_read_file(file, mp->shdrs, n,
			    mp->hdr.e_shoff) < 0)
				return (-1);
			if (get_syms(mp, file) < 0)
				return (-1);
			if (get_ctf(mp, file) < 0)
				return (-1);
			kobj_close_file(file);
			mp->flags |= KOBJ_RELOCATED;

		} else if (mp->flags & KOBJ_INTERP) {
			struct _buf *file;

			/*
			 * The interpreter path fragment in mp->filename
			 * will already have the module directory suffix
			 * in it (if appropriate).
			 */
			file = kobj_open_path(mp->filename, 1, 0);
			if (file == (struct _buf *)-1)
				return (-1);
			if (get_ctf(mp, file) < 0)
				return (-1);
			kobj_close_file(file);
			mp->flags |= KOBJ_RELOCATED;
		}
	}

	return (0);
}

static struct modctl *
mod_already_loaded(char *modname)
{
	struct modctl *mctl = kobj_modules;

	do {
		if (strcmp(modname, mctl->mod_filename) == 0)
			return (mctl);
		mctl = mctl->mod_next;

	} while (mctl != kobj_modules);

	return (NULL);
}

/*
 * Load all the primary dependent modules.
 */
static int
load_primary(struct module *mp, int lmid)
{
	struct modctl *cp;
	struct module *dmp;
	char *p, *q;
	char modname[MODMAXNAMELEN];

	if ((p = mp->depends_on) == NULL)
		return (0);

	/* CONSTANTCONDITION */
	while (1) {
		/*
		 * Skip space.
		 */
		while (*p && (*p == ' ' || *p == '\t'))
			p++;
		/*
		 * Get module name.
		 */
		q = modname;
		while (*p && *p != ' ' && *p != '\t')
			*q++ = *p++;

		if (q == modname)
			break;

		*q = '\0';
		/*
		 * Check for dup dependencies.
		 */
		if (strcmp(modname, "dtracestubs") == 0 ||
		    mod_already_loaded(modname) != NULL)
			continue;

		cp = add_primary(modname, lmid);
		cp->mod_busy = 1;
		/*
		 * Load it.
		 */
		(void) kobj_load_module(cp, 1);
		cp->mod_busy = 0;

		if ((dmp = cp->mod_mp) == NULL) {
			cp->mod_loaded = 0;
			cp->mod_installed = 0;
			cp->mod_loadcnt = 0;
			return (-1);
		}

		add_dependent(mp, dmp);
		dmp->flags |= KOBJ_PRIM;

		/*
		 * Recurse.
		 */
		if (load_primary(dmp, lmid) == -1) {
			cp->mod_loaded = 0;
			cp->mod_installed = 0;
			cp->mod_loadcnt = 0;
			return (-1);
		}
	}
	return (0);
}

static int
console_is_usb_serial(void)
{
	char *console;
	int len, ret;

	if ((len = BOP_GETPROPLEN(ops, "console")) == -1)
		return (0);

	console = kobj_zalloc(len, KM_WAIT|KM_TMP);
	(void) BOP_GETPROP(ops, "console", console);
	ret = (strcmp(console, "usb-serial") == 0);
	kobj_free(console, len);

	return (ret);
}

static int
load_kmdb(val_t *bootaux)
{
	struct modctl *mctl;
	struct module *mp;
	Sym *sym;

	if (console_is_usb_serial()) {
		_kobj_printf(ops, "kmdb not loaded "
		    "(unsupported on usb serial console)\n");
		return (0);
	}

	_kobj_printf(ops, "Loading kmdb...\n");

	if ((mctl = add_primary("misc/kmdbmod", KOBJ_LM_DEBUGGER)) == NULL)
		return (-1);

	mctl->mod_busy = 1;
	(void) kobj_load_module(mctl, 1);
	mctl->mod_busy = 0;

	if ((mp = mctl->mod_mp) == NULL)
		return (-1);

	mp->flags |= KOBJ_PRIM;

	if (load_primary(mp, KOBJ_LM_DEBUGGER) < 0)
		return (-1);

	if (boothowto & RB_VERBOSE)
		kobj_lm_dump(KOBJ_LM_DEBUGGER);

	if (bind_primary(bootaux, KOBJ_LM_DEBUGGER) < 0)
		return (-1);

	if ((sym = lookup_one(mctl->mod_mp, "kctl_boot_activate")) == NULL)
		return (-1);

#ifdef	KOBJ_DEBUG
	if (kobj_debug & D_DEBUG) {
		_kobj_printf(ops, "calling kctl_boot_activate() @ 0x%lx\n",
		    sym->st_value);
		_kobj_printf(ops, "\tops 0x%p\n", ops);
		_kobj_printf(ops, "\tromp 0x%p\n", romp);
	}
#endif

	if (((kctl_boot_activate_f *)sym->st_value)(ops, romp, 0,
	    (const char **)kobj_kmdb_argv) < 0)
		return (-1);

	return (0);
}

/*
 * Return a string listing module dependencies.
 */
static char *
depends_on(struct module *mp)
{
	Sym *sp;
	char *depstr, *q;

	/*
	 * The module doesn't have a depends_on value, so let's try it the
	 * old-fashioned way - via "_depends_on"
	 */
	if ((sp = lookup_one(mp, "_depends_on")) == NULL)
		return (NULL);

	q = (char *)sp->st_value;

	/*
	 * Idiot checks. Make sure it's
	 * in-bounds and NULL terminated.
	 */
	if (kobj_addrcheck(mp, q) || q[sp->st_size - 1] != '\0') {
		_kobj_printf(ops, "Error processing dependency for %s\n",
		    mp->filename);
		return (NULL);
	}

	depstr = (char *)kobj_alloc(strlen(q) + 1, KM_WAIT);
	(void) strcpy(depstr, q);

	return (depstr);
}

void
kobj_getmodinfo(void *xmp, struct modinfo *modinfo)
{
	struct module *mp;
	mp = (struct module *)xmp;

	modinfo->mi_base = mp->text;
	modinfo->mi_size = mp->text_size + mp->data_size;
}

/*
 * kobj_export_ksyms() performs the following services:
 *
 * (1) Migrates the symbol table from boot/kobj memory to the ksyms arena.
 * (2) Removes unneeded symbols to save space.
 * (3) Reduces memory footprint by using VM_BESTFIT allocations.
 * (4) Makes the symbol table visible to /dev/ksyms.
 */
static void
kobj_export_ksyms(struct module *mp)
{
	Sym *esp = (Sym *)(mp->symtbl + mp->symhdr->sh_size);
	Sym *sp, *osp;
	char *name;
	size_t namelen;
	struct module *omp;
	uint_t nsyms;
	size_t symsize = mp->symhdr->sh_entsize;
	size_t locals = 1;
	size_t strsize;

	/*
	 * Make a copy of the original module structure.
	 */
	omp = kobj_alloc(sizeof (struct module), KM_WAIT);
	bcopy(mp, omp, sizeof (struct module));

	/*
	 * Compute the sizes of the new symbol table sections.
	 */
	for (nsyms = strsize = 1, osp = (Sym *)omp->symtbl; osp < esp; osp++) {
		if (osp->st_value == 0)
			continue;
		if (sym_lookup(omp, osp) == NULL)
			continue;
		name = omp->strings + osp->st_name;
		namelen = strlen(name);
		if (ELF_ST_BIND(osp->st_info) == STB_LOCAL)
			locals++;
		nsyms++;
		strsize += namelen + 1;
	}

	mp->nsyms = nsyms;
	mp->hashsize = kobj_gethashsize(mp->nsyms);

	/*
	 * ksyms_lock must be held as writer during any operation that
	 * modifies ksyms_arena, including allocation from same, and
	 * must not be dropped until the arena is vmem_walk()able.
	 */
	rw_enter(&ksyms_lock, RW_WRITER);

	/*
	 * Allocate space for the new section headers (symtab and strtab),
	 * symbol table, buckets, chains, and strings.
	 */
	mp->symsize = (2 * sizeof (Shdr)) + (nsyms * symsize) +
	    (mp->hashsize + mp->nsyms) * sizeof (symid_t) + strsize;

	if (mp->flags & KOBJ_NOKSYMS) {
		mp->symspace = kobj_alloc(mp->symsize, KM_WAIT);
	} else {
		mp->symspace = vmem_alloc(ksyms_arena, mp->symsize,
		    VM_BESTFIT | VM_SLEEP);
	}
	bzero(mp->symspace, mp->symsize);

	/*
	 * Divvy up symspace.
	 */
	mp->shdrs = mp->symspace;
	mp->symhdr = (Shdr *)mp->shdrs;
	mp->strhdr = (Shdr *)(mp->symhdr + 1);
	mp->symtbl = (char *)(mp->strhdr + 1);
	mp->buckets = (symid_t *)(mp->symtbl + (nsyms * symsize));
	mp->chains = (symid_t *)(mp->buckets + mp->hashsize);
	mp->strings = (char *)(mp->chains + nsyms);

	/*
	 * Fill in the new section headers (symtab and strtab).
	 */
	mp->hdr.e_shnum = 2;
	mp->symtbl_section = 0;

	mp->symhdr->sh_type = SHT_SYMTAB;
	mp->symhdr->sh_addr = (Addr)mp->symtbl;
	mp->symhdr->sh_size = nsyms * symsize;
	mp->symhdr->sh_link = 1;
	mp->symhdr->sh_info = locals;
	mp->symhdr->sh_addralign = sizeof (Addr);
	mp->symhdr->sh_entsize = symsize;

	mp->strhdr->sh_type = SHT_STRTAB;
	mp->strhdr->sh_addr = (Addr)mp->strings;
	mp->strhdr->sh_size = strsize;
	mp->strhdr->sh_addralign = 1;

	/*
	 * Construct the new symbol table.
	 */
	for (nsyms = strsize = 1, osp = (Sym *)omp->symtbl; osp < esp; osp++) {
		if (osp->st_value == 0)
			continue;
		if (sym_lookup(omp, osp) == NULL)
			continue;
		name = omp->strings + osp->st_name;
		namelen = strlen(name);
		sp = (Sym *)(mp->symtbl + symsize * nsyms);
		bcopy(osp, sp, symsize);
		bcopy(name, mp->strings + strsize, namelen);
		sp->st_name = strsize;
		sym_insert(mp, name, nsyms);
		nsyms++;
		strsize += namelen + 1;
	}

	rw_exit(&ksyms_lock);

	/*
	 * Free the old section headers -- we'll never need them again.
	 */
	if (!(mp->flags & KOBJ_PRIM))
		kobj_free(omp->shdrs, omp->hdr.e_shentsize * omp->hdr.e_shnum);
	/*
	 * Discard the old symbol table and our copy of the module strucure.
	 */
	if (!(mp->flags & KOBJ_PRIM))
		kobj_free(omp->symspace, omp->symsize);
	kobj_free(omp, sizeof (struct module));
}

static void
kobj_export_ctf(struct module *mp)
{
	char *data = mp->ctfdata;
	size_t size = mp->ctfsize;

	if (data != NULL) {
		if (_moddebug & MODDEBUG_NOCTF) {
			mp->ctfdata = NULL;
			mp->ctfsize = 0;
		} else {
			mp->ctfdata = vmem_alloc(ctf_arena, size,
			    VM_BESTFIT | VM_SLEEP);
			bcopy(data, mp->ctfdata, size);
		}

		if (!(mp->flags & KOBJ_PRIM))
			kobj_free(data, size);
	}
}

void
kobj_export_module(struct module *mp)
{
	kobj_export_ksyms(mp);
	kobj_export_ctf(mp);

	mp->flags |= KOBJ_EXPORTED;
}

static int
process_dynamic(struct module *mp, char *dyndata, char *strdata)
{
	char *path = NULL, *depstr = NULL;
	int allocsize = 0, osize = 0, nsize = 0;
	char *libname, *tmp;
	int lsize;
	Dyn *dynp;

	for (dynp = (Dyn *)dyndata; dynp && dynp->d_tag != DT_NULL; dynp++) {
		switch (dynp->d_tag) {
		case DT_NEEDED:
			/*
			 * Read the DT_NEEDED entries, expanding the macros they
			 * contain (if any), and concatenating them into a
			 * single space-separated dependency list.
			 */
			libname = (ulong_t)dynp->d_un.d_ptr + strdata;

			if (strchr(libname, '$') != NULL) {
				char *_lib;

				if (path == NULL)
					path = kobj_alloc(MAXPATHLEN, KM_WAIT);
				if ((_lib = expand_libmacro(libname, path,
				    path)) != NULL)
					libname = _lib;
				else {
					_kobj_printf(ops, "krtld: "
					    "process_dynamic: failed to expand "
					    "%s\n", libname);
				}
			}

			lsize = strlen(libname);
			nsize += lsize;
			if (nsize + 1 > allocsize) {
				tmp = kobj_alloc(allocsize + MAXPATHLEN,
				    KM_WAIT);
				if (depstr != NULL) {
					bcopy(depstr, tmp, osize);
					kobj_free(depstr, allocsize);
				}
				depstr = tmp;
				allocsize += MAXPATHLEN;
			}
			bcopy(libname, depstr + osize, lsize);
			*(depstr + nsize) = ' '; /* separator */
			nsize++;
			osize = nsize;
			break;

		case DT_FLAGS_1:
			if (dynp->d_un.d_val & DF_1_IGNMULDEF)
				mp->flags |= KOBJ_IGNMULDEF;
			if (dynp->d_un.d_val & DF_1_NOKSYMS)
				mp->flags |= KOBJ_NOKSYMS;

			break;
		}
	}

	/*
	 * finish up the depends string (if any)
	 */
	if (depstr != NULL) {
		*(depstr + nsize - 1) = '\0'; /* overwrite seperator w/term */
		if (path != NULL)
			kobj_free(path, MAXPATHLEN);

		tmp = kobj_alloc(nsize, KM_WAIT);
		bcopy(depstr, tmp, nsize);
		kobj_free(depstr, allocsize);
		depstr = tmp;

		mp->depends_on = depstr;
	}

	return (0);
}

static int
do_dynamic(struct module *mp, struct _buf *file)
{
	Shdr *dshp, *dstrp, *shp;
	char *dyndata, *dstrdata;
	int dshn, shn, rc;

	/* find and validate the dynamic section (if any) */

	for (dshp = NULL, shn = 1; shn < mp->hdr.e_shnum; shn++) {
		shp = (Shdr *)(mp->shdrs + shn * mp->hdr.e_shentsize);
		switch (shp->sh_type) {
		case SHT_DYNAMIC:
			if (dshp != NULL) {
				_kobj_printf(ops, "krtld: get_dynamic: %s, ",
				    mp->filename);
				_kobj_printf(ops,
				    "multiple dynamic sections\n");
				return (-1);
			} else {
				dshp = shp;
				dshn = shn;
			}
			break;
		}
	}

	if (dshp == NULL)
		return (0);

	if (dshp->sh_link > mp->hdr.e_shnum) {
		_kobj_printf(ops, "krtld: get_dynamic: %s, ", mp->filename);
		_kobj_printf(ops, "no section for sh_link %d\n", dshp->sh_link);
		return (-1);
	}
	dstrp = (Shdr *)(mp->shdrs + dshp->sh_link * mp->hdr.e_shentsize);

	if (dstrp->sh_type != SHT_STRTAB) {
		_kobj_printf(ops, "krtld: get_dynamic: %s, ", mp->filename);
		_kobj_printf(ops, "sh_link not a string table for section %d\n",
		    dshn);
		return (-1);
	}

	/* read it from disk */

	dyndata = kobj_alloc(dshp->sh_size, KM_WAIT|KM_TMP);
	if (kobj_read_file(file, dyndata, dshp->sh_size, dshp->sh_offset) < 0) {
		_kobj_printf(ops, "krtld: get_dynamic: %s, ", mp->filename);
		_kobj_printf(ops, "error reading section %d\n", dshn);

		kobj_free(dyndata, dshp->sh_size);
		return (-1);
	}

	dstrdata = kobj_alloc(dstrp->sh_size, KM_WAIT|KM_TMP);
	if (kobj_read_file(file, dstrdata, dstrp->sh_size,
	    dstrp->sh_offset) < 0) {
		_kobj_printf(ops, "krtld: get_dynamic: %s, ", mp->filename);
		_kobj_printf(ops, "error reading section %d\n", dshp->sh_link);

		kobj_free(dyndata, dshp->sh_size);
		kobj_free(dstrdata, dstrp->sh_size);
		return (-1);
	}

	/* pull the interesting pieces out */

	rc = process_dynamic(mp, dyndata, dstrdata);

	kobj_free(dyndata, dshp->sh_size);
	kobj_free(dstrdata, dstrp->sh_size);

	return (rc);
}

void
kobj_set_ctf(struct module *mp, caddr_t data, size_t size)
{
	if (!standalone) {
		if (mp->ctfdata != NULL) {
			if (vmem_contains(ctf_arena, mp->ctfdata,
			    mp->ctfsize)) {
				vmem_free(ctf_arena, mp->ctfdata, mp->ctfsize);
			} else {
				kobj_free(mp->ctfdata, mp->ctfsize);
			}
		}
	}

	/*
	 * The order is very important here.  We need to make sure that
	 * consumers, at any given instant, see a consistent state.  We'd
	 * rather they see no CTF data than the address of one buffer and the
	 * size of another.
	 */
	mp->ctfdata = NULL;
	membar_producer();
	mp->ctfsize = size;
	mp->ctfdata = data;
	membar_producer();
}

int
kobj_load_module(struct modctl *modp, int use_path)
{
	char *filename = modp->mod_filename;
	char *modname = modp->mod_modname;
	int i;
	int n;
	struct _buf *file;
	struct module *mp = NULL;
#ifdef MODDIR_SUFFIX
	int no_suffixdir_drv = 0;
#endif

	mp = kobj_zalloc(sizeof (struct module), KM_WAIT);

	/*
	 * We need to prevent kmdb's symbols from leaking into /dev/ksyms.
	 * kmdb contains a bunch of symbols with well-known names, symbols
	 * which will mask the real versions, thus causing no end of trouble
	 * for mdb.
	 */
	if (strcmp(modp->mod_modname, "kmdbmod") == 0)
		mp->flags |= KOBJ_NOKSYMS;

	file = kobj_open_path(filename, use_path, 1);
	if (file == (struct _buf *)-1) {
#ifdef MODDIR_SUFFIX
		file = kobj_open_path(filename, use_path, 0);
#endif
		if (file == (struct _buf *)-1) {
			kobj_free(mp, sizeof (*mp));
			goto bad;
		}
#ifdef MODDIR_SUFFIX
		/*
		 * There is no driver module in the ISA specific (suffix)
		 * subdirectory but there is a module in the parent directory.
		 */
		if (strncmp(filename, "drv/", 4) == 0) {
			no_suffixdir_drv = 1;
		}
#endif
	}

	mp->filename = kobj_alloc(strlen(file->_name) + 1, KM_WAIT);
	(void) strcpy(mp->filename, file->_name);

	if (kobj_read_file(file, (char *)&mp->hdr, sizeof (mp->hdr), 0) < 0) {
		_kobj_printf(ops, "kobj_load_module: %s read header failed\n",
		    modname);
		kobj_free(mp->filename, strlen(file->_name) + 1);
		kobj_free(mp, sizeof (*mp));
		goto bad;
	}
	for (i = 0; i < SELFMAG; i++) {
		if (mp->hdr.e_ident[i] != ELFMAG[i]) {
			if (_moddebug & MODDEBUG_ERRMSG)
				_kobj_printf(ops, "%s not an elf module\n",
				    modname);
			kobj_free(mp->filename, strlen(file->_name) + 1);
			kobj_free(mp, sizeof (*mp));
			goto bad;
		}
	}
	/*
	 * It's ELF, but is it our ISA?  Interpreting the header
	 * from a file for a byte-swapped ISA could cause a huge
	 * and unsatisfiable value to be passed to kobj_alloc below
	 * and therefore hang booting.
	 */
	if (!elf_mach_ok(&mp->hdr)) {
		if (_moddebug & MODDEBUG_ERRMSG)
			_kobj_printf(ops, "%s not an elf module for this ISA\n",
			    modname);
		kobj_free(mp->filename, strlen(file->_name) + 1);
		kobj_free(mp, sizeof (*mp));
#ifdef MODDIR_SUFFIX
		/*
		 * The driver mod is not in the ISA specific subdirectory
		 * and the module in the parent directory is not our ISA.
		 * If it is our ISA, for now we will silently succeed.
		 */
		if (no_suffixdir_drv == 1) {
			cmn_err(CE_CONT, "?NOTICE: %s: 64-bit driver module"
			    " not found\n", modname);
		}
#endif
		goto bad;
	}

	/*
	 * All modules, save for unix, should be relocatable (as opposed to
	 * dynamic).  Dynamic modules come with PLTs and GOTs, which can't
	 * currently be processed by krtld.
	 */
	if (mp->hdr.e_type != ET_REL) {
		if (_moddebug & MODDEBUG_ERRMSG)
			_kobj_printf(ops, "%s isn't a relocatable (ET_REL) "
			    "module\n", modname);
		kobj_free(mp->filename, strlen(file->_name) + 1);
		kobj_free(mp, sizeof (*mp));
		goto bad;
	}

	n = mp->hdr.e_shentsize * mp->hdr.e_shnum;
	mp->shdrs = kobj_alloc(n, KM_WAIT);

	if (kobj_read_file(file, mp->shdrs, n, mp->hdr.e_shoff) < 0) {
		_kobj_printf(ops, "kobj_load_module: %s error reading "
		    "section headers\n", modname);
		kobj_free(mp->shdrs, n);
		kobj_free(mp->filename, strlen(file->_name) + 1);
		kobj_free(mp, sizeof (*mp));
		goto bad;
	}

	kobj_notify(KOBJ_NOTIFY_MODLOADING, modp);
	module_assign(modp, mp);

	/* read in sections */
	if (get_progbits(mp, file) < 0) {
		_kobj_printf(ops, "%s error reading sections\n", modname);
		goto bad;
	}

	if (do_dynamic(mp, file) < 0) {
		_kobj_printf(ops, "%s error reading dynamic section\n",
		    modname);
		goto bad;
	}

	modp->mod_text = mp->text;
	modp->mod_text_size = mp->text_size;

	/* read in symbols; adjust values for each section's real address */
	if (get_syms(mp, file) < 0) {
		_kobj_printf(ops, "%s error reading symbols\n",
		    modname);
		goto bad;
	}

	/*
	 * If we didn't dependency information from the dynamic section, look
	 * for it the old-fashioned way.
	 */
	if (mp->depends_on == NULL)
		mp->depends_on = depends_on(mp);

	if (get_ctf(mp, file) < 0) {
		_kobj_printf(ops, "%s debug information will not "
		    "be available\n", modname);
	}

	/* primary kernel modules do not have a signature section */
	if (!(mp->flags & KOBJ_PRIM))
		get_signature(mp, file);

#ifdef	KOBJ_DEBUG
	if (kobj_debug & D_LOADING) {
		_kobj_printf(ops, "krtld: file=%s\n", mp->filename);
		_kobj_printf(ops, "\ttext:0x%p", mp->text);
		_kobj_printf(ops, " size: 0x%x\n", mp->text_size);
		_kobj_printf(ops, "\tdata:0x%p", mp->data);
		_kobj_printf(ops, " dsize: 0x%x\n", mp->data_size);
	}
#endif /* KOBJ_DEBUG */

	/*
	 * For primary kernel modules, we defer
	 * symbol resolution and relocation until
	 * all primary objects have been loaded.
	 */
	if (!standalone) {
		int ddrval, dcrval;
		char *dependent_modname;
		/* load all dependents */
		dependent_modname = kobj_zalloc(MODMAXNAMELEN, KM_WAIT);
		ddrval = do_dependents(modp, dependent_modname, MODMAXNAMELEN);

		/*
		 * resolve undefined and common symbols,
		 * also allocates common space
		 */
		if ((dcrval = do_common(mp)) < 0) {
			switch (dcrval) {
			case DOSYM_UNSAFE:
				_kobj_printf(ops, "WARNING: mod_load: "
				    "MT-unsafe module '%s' rejected\n",
				    modname);
				break;
			case DOSYM_UNDEF:
				_kobj_printf(ops, "WARNING: mod_load: "
				    "cannot load module '%s'\n",
				    modname);
				if (ddrval == -1) {
					_kobj_printf(ops, "WARNING: %s: ",
					    modname);
					_kobj_printf(ops,
					    "unable to resolve dependency, "
					    "module '%s' not found\n",
					    dependent_modname);
				}
				break;
			}
		}
		kobj_free(dependent_modname, MODMAXNAMELEN);
		if (dcrval < 0)
			goto bad;

		/* process relocation tables */
		if (do_relocations(mp) < 0) {
			_kobj_printf(ops, "%s error doing relocations\n",
			    modname);
			goto bad;
		}

		if (mp->destination) {
			off_t	off = (uintptr_t)mp->destination & PAGEOFFSET;
			caddr_t	base = (caddr_t)mp->destination - off;
			size_t	size = P2ROUNDUP(mp->text_size + off, PAGESIZE);

			hat_unload(kas.a_hat, base, size, HAT_UNLOAD_UNLOCK);
			vmem_free(heap_arena, base, size);
		}

		/* sync_instruction_memory */
		kobj_sync_instruction_memory(mp->text, mp->text_size);
#ifdef	MPSAS
		sas_syms(mp);
#endif
		kobj_export_module(mp);
		kobj_notify(KOBJ_NOTIFY_MODLOADED, modp);
	}
	kobj_close_file(file);
	return (0);
bad:
	if (file != (struct _buf *)-1)
		kobj_close_file(file);
	if (modp->mod_mp != NULL)
		free_module_data(modp->mod_mp);

	module_assign(modp, NULL);
	return ((file == (struct _buf *)-1) ? ENOENT : EINVAL);
}

int
kobj_load_primary_module(struct modctl *modp)
{
	struct modctl *dep;
	struct module *mp;

	if (kobj_load_module(modp, 0) != 0)
		return (-1);

	mp = modp->mod_mp;
	mp->flags |= KOBJ_PRIM;

	/* Bind new module to its dependents */
	if (mp->depends_on != NULL && (dep =
	    mod_already_loaded(mp->depends_on)) == NULL) {
#ifdef	KOBJ_DEBUG
		if (kobj_debug & D_DEBUG) {
			_kobj_printf(ops, "krtld: failed to resolve deps "
			    "for primary %s\n", modp->mod_modname);
		}
#endif
		return (-1);
	}

	add_dependent(mp, dep->mod_mp);

	/*
	 * Relocate it.  This module may not be part of a link map, so we
	 * can't use bind_primary.
	 */
	if (do_common(mp) < 0 || do_symbols(mp, 0) < 0 ||
	    do_relocations(mp) < 0) {
#ifdef	KOBJ_DEBUG
		if (kobj_debug & D_DEBUG) {
			_kobj_printf(ops, "krtld: failed to relocate "
			    "primary %s\n", modp->mod_modname);
		}
#endif
		return (-1);
	}

	return (0);
}

static void
module_assign(struct modctl *cp, struct module *mp)
{
	if (standalone) {
		cp->mod_mp = mp;
		return;
	}
	mutex_enter(&mod_lock);
	cp->mod_mp = mp;
	cp->mod_gencount++;
	mutex_exit(&mod_lock);
}

void
kobj_unload_module(struct modctl *modp)
{
	struct module *mp = modp->mod_mp;

	if ((_moddebug & MODDEBUG_KEEPTEXT) && mp) {
		_kobj_printf(ops, "text for %s ", mp->filename);
		_kobj_printf(ops, "was at %p\n", mp->text);
		mp->text = NULL;	/* don't actually free it */
	}

	kobj_notify(KOBJ_NOTIFY_MODUNLOADING, modp);

	/*
	 * Null out mod_mp first, so consumers (debuggers) know not to look
	 * at the module structure any more.
	 */
	mutex_enter(&mod_lock);
	modp->mod_mp = NULL;
	mutex_exit(&mod_lock);

	kobj_notify(KOBJ_NOTIFY_MODUNLOADED, modp);
	free_module_data(mp);
}

static void
free_module_data(struct module *mp)
{
	struct module_list *lp, *tmp;
	int ksyms_exported = 0;

	lp = mp->head;
	while (lp) {
		tmp = lp;
		lp = lp->next;
		kobj_free((char *)tmp, sizeof (*tmp));
	}

	rw_enter(&ksyms_lock, RW_WRITER);
	if (mp->symspace) {
		if (vmem_contains(ksyms_arena, mp->symspace, mp->symsize)) {
			vmem_free(ksyms_arena, mp->symspace, mp->symsize);
			ksyms_exported = 1;
		} else {
			if (mp->flags & KOBJ_NOKSYMS)
				ksyms_exported = 1;
			kobj_free(mp->symspace, mp->symsize);
		}
	}
	rw_exit(&ksyms_lock);

	if (mp->ctfdata) {
		if (vmem_contains(ctf_arena, mp->ctfdata, mp->ctfsize))
			vmem_free(ctf_arena, mp->ctfdata, mp->ctfsize);
		else
			kobj_free(mp->ctfdata, mp->ctfsize);
	}

	if (mp->sigdata)
		kobj_free(mp->sigdata, mp->sigsize);

	/*
	 * We did not get far enough into kobj_export_ksyms() to free allocated
	 * buffers because we encounted error conditions. Free the buffers.
	 */
	if ((ksyms_exported == 0) && (mp->shdrs != NULL)) {
		uint_t shn;
		Shdr *shp;

		for (shn = 1; shn < mp->hdr.e_shnum; shn++) {
			shp = (Shdr *)(mp->shdrs + shn * mp->hdr.e_shentsize);
			switch (shp->sh_type) {
			case SHT_RELA:
			case SHT_REL:
				if (shp->sh_addr != 0)
					kobj_free((void *)shp->sh_addr,
					    shp->sh_size);
				break;
			}
		}
err_free_done:
		if (!(mp->flags & KOBJ_PRIM)) {
			kobj_free(mp->shdrs,
			    mp->hdr.e_shentsize * mp->hdr.e_shnum);
		}
	}

	if (mp->bss)
		vmem_free(data_arena, (void *)mp->bss, mp->bss_size);

	if (mp->fbt_tab)
		kobj_texthole_free(mp->fbt_tab, mp->fbt_size);

	if (mp->textwin_base)
		kobj_textwin_free(mp);

	if (mp->sdt_probes != NULL) {
		sdt_probedesc_t *sdp = mp->sdt_probes, *next;

		while (sdp != NULL) {
			next = sdp->sdpd_next;
			kobj_free(sdp->sdpd_name, strlen(sdp->sdpd_name) + 1);
			kobj_free(sdp, sizeof (sdt_probedesc_t));
			sdp = next;
		}
	}

	if (mp->sdt_tab)
		kobj_texthole_free(mp->sdt_tab, mp->sdt_size);
	if (mp->text)
		vmem_free(text_arena, mp->text, mp->text_size);
	if (mp->data)
		vmem_free(data_arena, mp->data, mp->data_size);
	if (mp->depends_on)
		kobj_free(mp->depends_on, strlen(mp->depends_on)+1);
	if (mp->filename)
		kobj_free(mp->filename, strlen(mp->filename)+1);

	kobj_free((char *)mp, sizeof (*mp));
}

static int
get_progbits(struct module *mp, struct _buf *file)
{
	struct proginfo *tp, *dp, *sdp;
	Shdr *shp;
	reloc_dest_t dest = NULL;
	uintptr_t bits_ptr;
	uintptr_t text = 0, data, sdata = 0, textptr;
	uint_t shn;
	int err = -1;

	tp = kobj_zalloc(sizeof (struct proginfo), KM_WAIT);
	dp = kobj_zalloc(sizeof (struct proginfo), KM_WAIT);
	sdp = kobj_zalloc(sizeof (struct proginfo), KM_WAIT);
	/*
	 * loop through sections to find out how much space we need
	 * for text, data, (also bss that is already assigned)
	 */
	if (get_progbits_size(mp, tp, dp, sdp) < 0)
		goto done;

	mp->text_size = tp->size;
	mp->data_size = dp->size;

	if (standalone) {
		caddr_t limit = _data;

		if (lg_pagesize && _text + lg_pagesize < limit)
			limit = _text + lg_pagesize;

		mp->text = kobj_segbrk(&_etext, mp->text_size,
		    tp->align, limit);
		/*
		 * If we can't grow the text segment, try the
		 * data segment before failing.
		 */
		if (mp->text == NULL) {
			mp->text = kobj_segbrk(&_edata, mp->text_size,
			    tp->align, 0);
		}

		mp->data = kobj_segbrk(&_edata, mp->data_size, dp->align, 0);

		if (mp->text == NULL || mp->data == NULL)
			goto done;

	} else {
		if (text_arena == NULL)
			kobj_vmem_init(&text_arena, &data_arena);

		/*
		 * some architectures may want to load the module on a
		 * page that is currently read only. It may not be
		 * possible for those architectures to remap their page
		 * on the fly. So we provide a facility for them to hang
		 * a private hook where the memory they assign the module
		 * is not the actual place where the module loads.
		 *
		 * In this case there are two addresses that deal with the
		 * modload.
		 * 1) the final destination of the module
		 * 2) the address that is used to view the newly
		 * loaded module until all the relocations relative to 1
		 * above are completed.
		 *
		 * That is what dest is used for below.
		 */
		mp->text_size += tp->align;
		mp->data_size += dp->align;

		mp->text = kobj_text_alloc(text_arena, mp->text_size);

		/*
		 * a remap is taking place. Align the text ptr relative
		 * to the secondary mapping. That is where the bits will
		 * be read in.
		 */
		if (kvseg.s_base != NULL && !vmem_contains(heaptext_arena,
		    mp->text, mp->text_size)) {
			off_t	off = (uintptr_t)mp->text & PAGEOFFSET;
			size_t	size = P2ROUNDUP(mp->text_size + off, PAGESIZE);
			caddr_t	map = vmem_alloc(heap_arena, size, VM_SLEEP);
			caddr_t orig = mp->text - off;
			pgcnt_t pages = size / PAGESIZE;

			dest = (reloc_dest_t)(map + off);
			text = ALIGN((uintptr_t)dest, tp->align);

			while (pages--) {
				hat_devload(kas.a_hat, map, PAGESIZE,
				    hat_getpfnum(kas.a_hat, orig),
				    PROT_READ | PROT_WRITE | PROT_EXEC,
				    HAT_LOAD_NOCONSIST | HAT_LOAD_LOCK);
				map += PAGESIZE;
				orig += PAGESIZE;
			}
			/*
			 * Since we set up a non-cacheable mapping, we need
			 * to flush any old entries in the cache that might
			 * be left around from the read-only mapping.
			 */
			dcache_flushall();
		}
		if (mp->data_size)
			mp->data = vmem_alloc(data_arena, mp->data_size,
			    VM_SLEEP | VM_BESTFIT);
	}
	textptr = (uintptr_t)mp->text;
	textptr = ALIGN(textptr, tp->align);
	mp->destination = dest;

	/*
	 * This is the case where a remap is not being done.
	 */
	if (text == 0)
		text = ALIGN((uintptr_t)mp->text, tp->align);
	data = ALIGN((uintptr_t)mp->data, dp->align);

	/* now loop though sections assigning addresses and loading the data */
	for (shn = 1; shn < mp->hdr.e_shnum; shn++) {
		shp = (Shdr *)(mp->shdrs + shn * mp->hdr.e_shentsize);
		if (!(shp->sh_flags & SHF_ALLOC))
			continue;

		if ((shp->sh_flags & SHF_WRITE) == 0)
			bits_ptr = text;
		else if (shp->sh_flags & SHF_NEUT_SHORT)
			bits_ptr = sdata;
		else
			bits_ptr = data;

		bits_ptr = ALIGN(bits_ptr, shp->sh_addralign);

		if (shp->sh_type == SHT_NOBITS) {
			/*
			 * Zero bss.
			 */
			bzero((caddr_t)bits_ptr, shp->sh_size);
			shp->sh_type = SHT_PROGBITS;
		} else {
			if (kobj_read_file(file, (char *)bits_ptr,
			    shp->sh_size, shp->sh_offset) < 0)
				goto done;
		}

		if (shp->sh_flags & SHF_WRITE) {
			shp->sh_addr = bits_ptr;
		} else {
			textptr = ALIGN(textptr, shp->sh_addralign);
			shp->sh_addr = textptr;
			textptr += shp->sh_size;
		}

		bits_ptr += shp->sh_size;
		if ((shp->sh_flags & SHF_WRITE) == 0)
			text = bits_ptr;
		else if (shp->sh_flags & SHF_NEUT_SHORT)
			sdata = bits_ptr;
		else
			data = bits_ptr;
	}

	err = 0;
done:
	/*
	 * Free and mark as freed the section headers here so that
	 * free_module_data() does not have to worry about this buffer.
	 *
	 * This buffer is freed here because one of the possible reasons
	 * for error is a section with non-zero sh_addr and in that case
	 * free_module_data() would have no way of recognizing that this
	 * buffer was unallocated.
	 */
	if (err != 0) {
		kobj_free(mp->shdrs, mp->hdr.e_shentsize * mp->hdr.e_shnum);
		mp->shdrs = NULL;
	}

	(void) kobj_free(tp, sizeof (struct proginfo));
	(void) kobj_free(dp, sizeof (struct proginfo));
	(void) kobj_free(sdp, sizeof (struct proginfo));

	return (err);
}

/*
 * Go through suppress_sym_list to see if "multiply defined"
 * warning of this symbol should be suppressed.  Return 1 if
 * warning should be suppressed, 0 otherwise.
 */
static int
kobj_suppress_warning(char *symname)
{
	int	i;

	for (i = 0; suppress_sym_list[i] != NULL; i++) {
		if (strcmp(suppress_sym_list[i], symname) == 0)
			return (1);
	}

	return (0);
}

static int
get_syms(struct module *mp, struct _buf *file)
{
	uint_t		shn;
	Shdr	*shp;
	uint_t		i;
	Sym	*sp, *ksp;
	char		*symname;
	int		dosymtab = 0;
	extern char 	stubs_base[], stubs_end[];

	/*
	 * Find the interesting sections.
	 */
	for (shn = 1; shn < mp->hdr.e_shnum; shn++) {
		shp = (Shdr *)(mp->shdrs + shn * mp->hdr.e_shentsize);
		switch (shp->sh_type) {
		case SHT_SYMTAB:
			mp->symtbl_section = shn;
			mp->symhdr = shp;
			dosymtab++;
			break;

		case SHT_RELA:
		case SHT_REL:
			/*
			 * Already loaded.
			 */
			if (shp->sh_addr)
				continue;
			shp->sh_addr = (Addr)
			    kobj_alloc(shp->sh_size, KM_WAIT|KM_TMP);

			if (kobj_read_file(file, (char *)shp->sh_addr,
			    shp->sh_size, shp->sh_offset) < 0) {
				_kobj_printf(ops, "krtld: get_syms: %s, ",
				    mp->filename);
				_kobj_printf(ops, "error reading section %d\n",
				    shn);
				return (-1);
			}
			break;
		}
	}

	/*
	 * This is true for a stripped executable.  In the case of
	 * 'unix' it can be stripped but it still contains the SHT_DYNSYM,
	 * and since that symbol information is still present everything
	 * is just fine.
	 */
	if (!dosymtab) {
		if (mp->flags & KOBJ_EXEC)
			return (0);
		_kobj_printf(ops, "krtld: get_syms: %s ",
		    mp->filename);
		_kobj_printf(ops, "no SHT_SYMTAB symbol table found\n");
		return (-1);
	}

	/*
	 * get the associated string table header
	 */
	if ((mp->symhdr == 0) || (mp->symhdr->sh_link >= mp->hdr.e_shnum))
		return (-1);
	mp->strhdr = (Shdr *)
	    (mp->shdrs + mp->symhdr->sh_link * mp->hdr.e_shentsize);

	mp->nsyms = mp->symhdr->sh_size / mp->symhdr->sh_entsize;
	mp->hashsize = kobj_gethashsize(mp->nsyms);

	/*
	 * Allocate space for the symbol table, buckets, chains, and strings.
	 */
	mp->symsize = mp->symhdr->sh_size +
	    (mp->hashsize + mp->nsyms) * sizeof (symid_t) + mp->strhdr->sh_size;
	mp->symspace = kobj_zalloc(mp->symsize, KM_WAIT|KM_SCRATCH);

	mp->symtbl = mp->symspace;
	mp->buckets = (symid_t *)(mp->symtbl + mp->symhdr->sh_size);
	mp->chains = mp->buckets + mp->hashsize;
	mp->strings = (char *)(mp->chains + mp->nsyms);

	if (kobj_read_file(file, mp->symtbl,
	    mp->symhdr->sh_size, mp->symhdr->sh_offset) < 0 ||
	    kobj_read_file(file, mp->strings,
	    mp->strhdr->sh_size, mp->strhdr->sh_offset) < 0)
		return (-1);

	/*
	 * loop through the symbol table adjusting values to account
	 * for where each section got loaded into memory.  Also
	 * fill in the hash table.
	 */
	for (i = 1; i < mp->nsyms; i++) {
		sp = (Sym *)(mp->symtbl + i * mp->symhdr->sh_entsize);
		if (sp->st_shndx < SHN_LORESERVE) {
			if (sp->st_shndx >= mp->hdr.e_shnum) {
				_kobj_printf(ops, "%s bad shndx ",
				    file->_name);
				_kobj_printf(ops, "in symbol %d\n", i);
				return (-1);
			}
			shp = (Shdr *)
			    (mp->shdrs +
			    sp->st_shndx * mp->hdr.e_shentsize);
			if (!(mp->flags & KOBJ_EXEC))
				sp->st_value += shp->sh_addr;
		}

		if (sp->st_name == 0 || sp->st_shndx == SHN_UNDEF)
			continue;
		if (sp->st_name >= mp->strhdr->sh_size)
			return (-1);

		symname = mp->strings + sp->st_name;

		if (!(mp->flags & KOBJ_EXEC) &&
		    ELF_ST_BIND(sp->st_info) == STB_GLOBAL) {
			ksp = kobj_lookup_all(mp, symname, 0);

			if (ksp && ELF_ST_BIND(ksp->st_info) == STB_GLOBAL &&
			    !kobj_suppress_warning(symname) &&
			    sp->st_shndx != SHN_UNDEF &&
			    sp->st_shndx != SHN_COMMON &&
			    ksp->st_shndx != SHN_UNDEF &&
			    ksp->st_shndx != SHN_COMMON) {
				/*
				 * Unless this symbol is a stub, it's multiply
				 * defined.  Multiply-defined symbols are
				 * usually bad, but some objects (kmdb) have
				 * a legitimate need to have their own
				 * copies of common functions.
				 */
				if ((standalone ||
				    ksp->st_value < (uintptr_t)stubs_base ||
				    ksp->st_value >= (uintptr_t)stubs_end) &&
				    !(mp->flags & KOBJ_IGNMULDEF)) {
					_kobj_printf(ops,
					    "%s symbol ", file->_name);
					_kobj_printf(ops,
					    "%s multiply defined\n", symname);
				}
			}
		}

		sym_insert(mp, symname, i);
	}

	return (0);
}

static int
get_ctf(struct module *mp, struct _buf *file)
{
	char *shstrtab, *ctfdata;
	size_t shstrlen;
	Shdr *shp;
	uint_t i;

	if (_moddebug & MODDEBUG_NOCTF)
		return (0); /* do not attempt to even load CTF data */

	if (mp->hdr.e_shstrndx >= mp->hdr.e_shnum) {
		_kobj_printf(ops, "krtld: get_ctf: %s, ",
		    mp->filename);
		_kobj_printf(ops, "corrupt e_shstrndx %u\n",
		    mp->hdr.e_shstrndx);
		return (-1);
	}

	shp = (Shdr *)(mp->shdrs + mp->hdr.e_shstrndx * mp->hdr.e_shentsize);
	shstrlen = shp->sh_size;
	shstrtab = kobj_alloc(shstrlen, KM_WAIT|KM_TMP);

	if (kobj_read_file(file, shstrtab, shstrlen, shp->sh_offset) < 0) {
		_kobj_printf(ops, "krtld: get_ctf: %s, ",
		    mp->filename);
		_kobj_printf(ops, "error reading section %u\n",
		    mp->hdr.e_shstrndx);
		kobj_free(shstrtab, shstrlen);
		return (-1);
	}

	for (i = 0; i < mp->hdr.e_shnum; i++) {
		shp = (Shdr *)(mp->shdrs + i * mp->hdr.e_shentsize);

		if (shp->sh_size != 0 && shp->sh_name < shstrlen &&
		    strcmp(shstrtab + shp->sh_name, ".SUNW_ctf") == 0) {
			ctfdata = kobj_alloc(shp->sh_size, KM_WAIT|KM_SCRATCH);

			if (kobj_read_file(file, ctfdata, shp->sh_size,
			    shp->sh_offset) < 0) {
				_kobj_printf(ops, "krtld: get_ctf: %s, error "
				    "reading .SUNW_ctf data\n", mp->filename);
				kobj_free(ctfdata, shp->sh_size);
				kobj_free(shstrtab, shstrlen);
				return (-1);
			}

			mp->ctfdata = ctfdata;
			mp->ctfsize = shp->sh_size;
			break;
		}
	}

	kobj_free(shstrtab, shstrlen);
	return (0);
}

#define	SHA1_DIGEST_LENGTH	20	/* SHA1 digest length in bytes */

/*
 * Return the hash of the ELF sections that are memory resident.
 * i.e. text and data.  We skip a SHT_NOBITS section since it occupies
 * no space in the file. We use SHA1 here since libelfsign uses
 * it and both places need to use the same algorithm.
 */
static void
crypto_es_hash(struct module *mp, char *hash, char *shstrtab)
{
	uint_t shn;
	Shdr *shp;
	SHA1_CTX ctx;

	SHA1Init(&ctx);

	for (shn = 1; shn < mp->hdr.e_shnum; shn++) {
		shp = (Shdr *)(mp->shdrs + shn * mp->hdr.e_shentsize);
		if (!(shp->sh_flags & SHF_ALLOC) || shp->sh_size == 0)
			continue;

		/*
		 * The check should ideally be shp->sh_type == SHT_NOBITS.
		 * However, we can't do that check here as get_progbits()
		 * resets the type.
		 */
		if (strcmp(shstrtab + shp->sh_name, ".bss") == 0)
			continue;
#ifdef	KOBJ_DEBUG
		if (kobj_debug & D_DEBUG)
			_kobj_printf(ops,
			    "krtld: crypto_es_hash: updating hash with"
			    " %s data size=%d\n", shstrtab + shp->sh_name,
			    shp->sh_size);
#endif
		ASSERT(shp->sh_addr != NULL);
		SHA1Update(&ctx, (const uint8_t *)shp->sh_addr, shp->sh_size);
	}

	SHA1Final((uchar_t *)hash, &ctx);
}

/*
 * Get the .SUNW_signature section for the module, it it exists.
 *
 * This section exists only for crypto modules. None of the
 * primary modules have this section currently.
 */
static void
get_signature(struct module *mp, struct _buf *file)
{
	char *shstrtab, *sigdata = NULL;
	size_t shstrlen;
	Shdr *shp;
	uint_t i;

	if (mp->hdr.e_shstrndx >= mp->hdr.e_shnum) {
		_kobj_printf(ops, "krtld: get_signature: %s, ",
		    mp->filename);
		_kobj_printf(ops, "corrupt e_shstrndx %u\n",
		    mp->hdr.e_shstrndx);
		return;
	}

	shp = (Shdr *)(mp->shdrs + mp->hdr.e_shstrndx * mp->hdr.e_shentsize);
	shstrlen = shp->sh_size;
	shstrtab = kobj_alloc(shstrlen, KM_WAIT|KM_TMP);

	if (kobj_read_file(file, shstrtab, shstrlen, shp->sh_offset) < 0) {
		_kobj_printf(ops, "krtld: get_signature: %s, ",
		    mp->filename);
		_kobj_printf(ops, "error reading section %u\n",
		    mp->hdr.e_shstrndx);
		kobj_free(shstrtab, shstrlen);
		return;
	}

	for (i = 0; i < mp->hdr.e_shnum; i++) {
		shp = (Shdr *)(mp->shdrs + i * mp->hdr.e_shentsize);
		if (shp->sh_size != 0 && shp->sh_name < shstrlen &&
		    strcmp(shstrtab + shp->sh_name,
		    ELF_SIGNATURE_SECTION) == 0) {
			filesig_vers_t filesig_version;
			size_t sigsize = shp->sh_size + SHA1_DIGEST_LENGTH;
			sigdata = kobj_alloc(sigsize, KM_WAIT|KM_SCRATCH);

			if (kobj_read_file(file, sigdata, shp->sh_size,
			    shp->sh_offset) < 0) {
				_kobj_printf(ops, "krtld: get_signature: %s,"
				    " error reading .SUNW_signature data\n",
				    mp->filename);
				kobj_free(sigdata, sigsize);
				kobj_free(shstrtab, shstrlen);
				return;
			}
			filesig_version = ((struct filesignatures *)sigdata)->
			    filesig_sig.filesig_version;
			if (!(filesig_version == FILESIG_VERSION1 ||
			    filesig_version == FILESIG_VERSION3)) {
				/* skip versions we don't understand */
				kobj_free(sigdata, sigsize);
				kobj_free(shstrtab, shstrlen);
				return;
			}

			mp->sigdata = sigdata;
			mp->sigsize = sigsize;
			break;
		}
	}

	if (sigdata != NULL) {
		crypto_es_hash(mp, sigdata + shp->sh_size, shstrtab);
	}

	kobj_free(shstrtab, shstrlen);
}

static void
add_dependent(struct module *mp, struct module *dep)
{
	struct module_list *lp;

	for (lp = mp->head; lp; lp = lp->next) {
		if (lp->mp == dep)
			return;	/* already on the list */
	}

	if (lp == NULL) {
		lp = kobj_zalloc(sizeof (*lp), KM_WAIT);

		lp->mp = dep;
		lp->next = NULL;
		if (mp->tail)
			mp->tail->next = lp;
		else
			mp->head = lp;
		mp->tail = lp;
	}
}

static int
do_dependents(struct modctl *modp, char *modname, size_t modnamelen)
{
	struct module *mp;
	struct modctl *req;
	char *d, *p, *q;
	int c;
	char *err_modname = NULL;

	mp = modp->mod_mp;

	if ((p = mp->depends_on) == NULL)
		return (0);

	for (;;) {
		/*
		 * Skip space.
		 */
		while (*p && (*p == ' ' || *p == '\t'))
			p++;
		/*
		 * Get module name.
		 */
		d = p;
		q = modname;
		c = 0;
		while (*p && *p != ' ' && *p != '\t') {
			if (c < modnamelen - 1) {
				*q++ = *p;
				c++;
			}
			p++;
		}

		if (q == modname)
			break;

		if (c == modnamelen - 1) {
			char *dep = kobj_alloc(p - d + 1, KM_WAIT|KM_TMP);

			(void) strncpy(dep, d,  p - d + 1);
			dep[p - d] = '\0';

			_kobj_printf(ops, "%s: dependency ", modp->mod_modname);
			_kobj_printf(ops, "'%s' too long ", dep);
			_kobj_printf(ops, "(max %d chars)\n", modnamelen);

			kobj_free(dep, p - d + 1);

			return (-1);
		}

		*q = '\0';
		if ((req = mod_load_requisite(modp, modname)) == NULL) {
#ifndef	KOBJ_DEBUG
			if (_moddebug & MODDEBUG_LOADMSG) {
#endif	/* KOBJ_DEBUG */
				_kobj_printf(ops,
				    "%s: unable to resolve dependency, ",
				    modp->mod_modname);
				_kobj_printf(ops, "cannot load module '%s'\n",
				    modname);
#ifndef	KOBJ_DEBUG
			}
#endif	/* KOBJ_DEBUG */
			if (err_modname == NULL) {
				/*
				 * This must be the same size as the modname
				 * one.
				 */
				err_modname = kobj_zalloc(MODMAXNAMELEN,
				    KM_WAIT);

				/*
				 * We can use strcpy() here without fearing
				 * the NULL terminator because the size of
				 * err_modname is the same as one of modname,
				 * and it's filled with zeros.
				 */
				(void) strcpy(err_modname, modname);
			}
			continue;
		}

		add_dependent(mp, req->mod_mp);
		mod_release_mod(req);

	}

	if (err_modname != NULL) {
		/*
		 * Copy the first module name where you detect an error to keep
		 * its behavior the same as before.
		 * This way keeps minimizing the memory use for error
		 * modules, and this might be important at boot time because
		 * the memory usage is a crucial factor for booting in most
		 * cases. You can expect more verbose messages when using
		 * a debug kernel or setting a bit in moddebug.
		 */
		bzero(modname, MODMAXNAMELEN);
		(void) strcpy(modname, err_modname);
		kobj_free(err_modname, MODMAXNAMELEN);
		return (-1);
	}

	return (0);
}

static int
do_common(struct module *mp)
{
	int err;

	/*
	 * first time through, assign all symbols defined in other
	 * modules, and count up how much common space will be needed
	 * (bss_size and bss_align)
	 */
	if ((err = do_symbols(mp, 0)) < 0)
		return (err);
	/*
	 * increase bss_size by the maximum delta that could be
	 * computed by the ALIGN below
	 */
	mp->bss_size += mp->bss_align;
	if (mp->bss_size) {
		if (standalone)
			mp->bss = (uintptr_t)kobj_segbrk(&_edata, mp->bss_size,
			    MINALIGN, 0);
		else
			mp->bss = (uintptr_t)vmem_alloc(data_arena,
			    mp->bss_size, VM_SLEEP | VM_BESTFIT);
		bzero((void *)mp->bss, mp->bss_size);
		/* now assign addresses to all common symbols */
		if ((err = do_symbols(mp, ALIGN(mp->bss, mp->bss_align))) < 0)
			return (err);
	}
	return (0);
}

static int
do_symbols(struct module *mp, Elf64_Addr bss_base)
{
	int bss_align;
	uintptr_t bss_ptr;
	int err;
	int i;
	Sym *sp, *sp1;
	char *name;
	int assign;
	int resolved = 1;

	/*
	 * Nothing left to do (optimization).
	 */
	if (mp->flags & KOBJ_RESOLVED)
		return (0);

	assign = (bss_base) ? 1 : 0;
	bss_ptr = bss_base;
	bss_align = 0;
	err = 0;

	for (i = 1; i < mp->nsyms; i++) {
		sp = (Sym *)(mp->symtbl + mp->symhdr->sh_entsize * i);
		/*
		 * we know that st_name is in bounds, since get_sections
		 * has already checked all of the symbols
		 */
		name = mp->strings + sp->st_name;
		if (sp->st_shndx != SHN_UNDEF && sp->st_shndx != SHN_COMMON)
			continue;
#ifdef	__sparc
		/*
		 * Register symbols are ignored in the kernel
		 */
		if (ELF_ST_TYPE(sp->st_info) == STT_SPARC_REGISTER) {
			if (*name != '\0') {
				_kobj_printf(ops, "%s: named REGISTER symbol ",
				    mp->filename);
				_kobj_printf(ops, "not supported '%s'\n",
				    name);
				err = DOSYM_UNDEF;
			}
			continue;
		}
#endif	/* __sparc */
		/*
		 * TLS symbols are ignored in the kernel
		 */
		if (ELF_ST_TYPE(sp->st_info) == STT_TLS) {
			_kobj_printf(ops, "%s: TLS symbol ",
			    mp->filename);
			_kobj_printf(ops, "not supported '%s'\n",
			    name);
			err = DOSYM_UNDEF;
			continue;
		}

		if (ELF_ST_BIND(sp->st_info) != STB_LOCAL) {
			if ((sp1 = kobj_lookup_all(mp, name, 0)) != NULL) {
				sp->st_shndx = SHN_ABS;
				sp->st_value = sp1->st_value;
				continue;
			}
		}

		if (sp->st_shndx == SHN_UNDEF) {
			resolved = 0;

			if (strncmp(name, sdt_prefix, strlen(sdt_prefix)) == 0)
				continue;

			/*
			 * If it's not a weak reference and it's
			 * not a primary object, it's an error.
			 * (Primary objects may take more than
			 * one pass to resolve)
			 */
			if (!(mp->flags & KOBJ_PRIM) &&
			    ELF_ST_BIND(sp->st_info) != STB_WEAK) {
				_kobj_printf(ops, "%s: undefined symbol",
				    mp->filename);
				_kobj_printf(ops, " '%s'\n", name);
				/*
				 * Try to determine whether this symbol
				 * represents a dependency on obsolete
				 * unsafe driver support.  This is just
				 * to make the warning more informative.
				 */
				if (strcmp(name, "sleep") == 0 ||
				    strcmp(name, "unsleep") == 0 ||
				    strcmp(name, "wakeup") == 0 ||
				    strcmp(name, "bsd_compat_ioctl") == 0 ||
				    strcmp(name, "unsafe_driver") == 0 ||
				    strncmp(name, "spl", 3) == 0 ||
				    strncmp(name, "i_ddi_spl", 9) == 0)
					err = DOSYM_UNSAFE;
				if (err == 0)
					err = DOSYM_UNDEF;
			}
			continue;
		}
		/*
		 * It's a common symbol - st_value is the
		 * required alignment.
		 */
		if (sp->st_value > bss_align)
			bss_align = sp->st_value;
		bss_ptr = ALIGN(bss_ptr, sp->st_value);
		if (assign) {
			sp->st_shndx = SHN_ABS;
			sp->st_value = bss_ptr;
		}
		bss_ptr += sp->st_size;
	}
	if (err)
		return (err);
	if (assign == 0 && mp->bss == NULL) {
		mp->bss_align = bss_align;
		mp->bss_size = bss_ptr;
	} else if (resolved) {
		mp->flags |= KOBJ_RESOLVED;
	}

	return (0);
}

uint_t
kobj_hash_name(const char *p)
{
	unsigned int g;
	uint_t hval;

	hval = 0;
	while (*p) {
		hval = (hval << 4) + *p++;
		if ((g = (hval & 0xf0000000)) != 0)
			hval ^= g >> 24;
		hval &= ~g;
	}
	return (hval);
}

/* look for name in all modules */
uintptr_t
kobj_getsymvalue(char *name, int kernelonly)
{
	Sym		*sp;
	struct modctl	*modp;
	struct module	*mp;
	uintptr_t	value = 0;

	if ((sp = kobj_lookup_kernel(name)) != NULL)
		return ((uintptr_t)sp->st_value);

	if (kernelonly)
		return (0);	/* didn't find it in the kernel so give up */

	mutex_enter(&mod_lock);
	modp = &modules;
	do {
		mp = (struct module *)modp->mod_mp;
		if (mp && !(mp->flags & KOBJ_PRIM) && modp->mod_loaded &&
		    (sp = lookup_one(mp, name))) {
			value = (uintptr_t)sp->st_value;
			break;
		}
	} while ((modp = modp->mod_next) != &modules);
	mutex_exit(&mod_lock);
	return (value);
}

/* look for a symbol near value. */
char *
kobj_getsymname(uintptr_t value, ulong_t *offset)
{
	char *name = NULL;
	struct modctl *modp;

	struct modctl_list *lp;
	struct module *mp;

	/*
	 * Loop through the primary kernel modules.
	 */
	for (lp = kobj_lm_lookup(KOBJ_LM_PRIMARY); lp; lp = lp->modl_next) {
		mp = mod(lp);

		if ((name = kobj_searchsym(mp, value, offset)) != NULL)
			return (name);
	}

	mutex_enter(&mod_lock);
	modp = &modules;
	do {
		mp = (struct module *)modp->mod_mp;
		if (mp && !(mp->flags & KOBJ_PRIM) && modp->mod_loaded &&
		    (name = kobj_searchsym(mp, value, offset)))
			break;
	} while ((modp = modp->mod_next) != &modules);
	mutex_exit(&mod_lock);
	return (name);
}

/* return address of symbol and size */

uintptr_t
kobj_getelfsym(char *name, void *mp, int *size)
{
	Sym *sp;

	if (mp == NULL)
		sp = kobj_lookup_kernel(name);
	else
		sp = lookup_one(mp, name);

	if (sp == NULL)
		return (0);

	*size = (int)sp->st_size;
	return ((uintptr_t)sp->st_value);
}

uintptr_t
kobj_lookup(struct module *mod, const char *name)
{
	Sym *sp;

	sp = lookup_one(mod, name);

	if (sp == NULL)
		return (0);

	return ((uintptr_t)sp->st_value);
}

char *
kobj_searchsym(struct module *mp, uintptr_t value, ulong_t *offset)
{
	Sym *symtabptr;
	char *strtabptr;
	int symnum;
	Sym *sym;
	Sym *cursym;
	uintptr_t curval;

	*offset = (ulong_t)-1l;		/* assume not found */
	cursym  = NULL;

	if (kobj_addrcheck(mp, (void *)value) != 0)
		return (NULL);		/* not in this module */

	strtabptr  = mp->strings;
	symtabptr  = (Sym *)mp->symtbl;

	/*
	 * Scan the module's symbol table for a symbol <= value
	 */
	for (symnum = 1, sym = symtabptr + 1;
	    symnum < mp->nsyms; symnum++, sym = (Sym *)
	    ((uintptr_t)sym + mp->symhdr->sh_entsize)) {
		if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL) {
			if (ELF_ST_BIND(sym->st_info) != STB_LOCAL)
				continue;
			if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
			    ELF_ST_TYPE(sym->st_info) != STT_FUNC)
				continue;
		}

		curval = (uintptr_t)sym->st_value;

		if (curval > value)
			continue;

		/*
		 * If one or both are functions...
		 */
		if (ELF_ST_TYPE(sym->st_info) == STT_FUNC || (cursym != NULL &&
		    ELF_ST_TYPE(cursym->st_info) == STT_FUNC)) {
			/* Ignore if the address is out of the bounds */
			if (value - sym->st_value >= sym->st_size)
				continue;

			if (cursym != NULL &&
			    ELF_ST_TYPE(cursym->st_info) == STT_FUNC) {
				/* Prefer the function to the non-function */
				if (ELF_ST_TYPE(sym->st_info) != STT_FUNC)
					continue;

				/* Prefer the larger of the two functions */
				if (sym->st_size <= cursym->st_size)
					continue;
			}
		} else if (value - curval >= *offset) {
			continue;
		}

		*offset = (ulong_t)(value - curval);
		cursym = sym;
	}
	if (cursym == NULL)
		return (NULL);

	return (strtabptr + cursym->st_name);
}

Sym *
kobj_lookup_all(struct module *mp, char *name, int include_self)
{
	Sym *sp;
	struct module_list *mlp;
	struct modctl_list *clp;
	struct module *mmp;

	if (include_self && (sp = lookup_one(mp, name)) != NULL)
		return (sp);

	for (mlp = mp->head; mlp; mlp = mlp->next) {
		if ((sp = lookup_one(mlp->mp, name)) != NULL &&
		    ELF_ST_BIND(sp->st_info) != STB_LOCAL)
			return (sp);
	}

	/*
	 * Loop through the primary kernel modules.
	 */
	for (clp = kobj_lm_lookup(KOBJ_LM_PRIMARY); clp; clp = clp->modl_next) {
		mmp = mod(clp);

		if (mmp == NULL || mp == mmp)
			continue;

		if ((sp = lookup_one(mmp, name)) != NULL &&
		    ELF_ST_BIND(sp->st_info) != STB_LOCAL)
			return (sp);
	}
	return (NULL);
}

Sym *
kobj_lookup_kernel(const char *name)
{
	struct modctl_list *lp;
	struct module *mp;
	Sym *sp;

	/*
	 * Loop through the primary kernel modules.
	 */
	for (lp = kobj_lm_lookup(KOBJ_LM_PRIMARY); lp; lp = lp->modl_next) {
		mp = mod(lp);

		if (mp == NULL)
			continue;

		if ((sp = lookup_one(mp, name)) != NULL)
			return (sp);
	}
	return (NULL);
}

static Sym *
lookup_one(struct module *mp, const char *name)
{
	symid_t *ip;
	char *name1;
	Sym *sp;

	for (ip = &mp->buckets[kobj_hash_name(name) % mp->hashsize]; *ip;
	    ip = &mp->chains[*ip]) {
		sp = (Sym *)(mp->symtbl +
		    mp->symhdr->sh_entsize * *ip);
		name1 = mp->strings + sp->st_name;
		if (strcmp(name, name1) == 0 &&
		    ELF_ST_TYPE(sp->st_info) != STT_FILE &&
		    sp->st_shndx != SHN_UNDEF &&
		    sp->st_shndx != SHN_COMMON)
			return (sp);
	}
	return (NULL);
}

/*
 * Lookup a given symbol pointer in the module's symbol hash.  If the symbol
 * is hashed, return the symbol pointer; otherwise return NULL.
 */
static Sym *
sym_lookup(struct module *mp, Sym *ksp)
{
	char *name = mp->strings + ksp->st_name;
	symid_t *ip;
	Sym *sp;

	for (ip = &mp->buckets[kobj_hash_name(name) % mp->hashsize]; *ip;
	    ip = &mp->chains[*ip]) {
		sp = (Sym *)(mp->symtbl + mp->symhdr->sh_entsize * *ip);
		if (sp == ksp)
			return (ksp);
	}
	return (NULL);
}

static void
sym_insert(struct module *mp, char *name, symid_t index)
{
	symid_t *ip;

#ifdef KOBJ_DEBUG
		if (kobj_debug & D_SYMBOLS) {
			static struct module *lastmp = NULL;
			Sym *sp;
			if (lastmp != mp) {
				_kobj_printf(ops,
				    "krtld: symbol entry: file=%s\n",
				    mp->filename);
				_kobj_printf(ops,
				    "krtld:\tsymndx\tvalue\t\t"
				    "symbol name\n");
				lastmp = mp;
			}
			sp = (Sym *)(mp->symtbl +
			    index * mp->symhdr->sh_entsize);
			_kobj_printf(ops, "krtld:\t[%3d]", index);
			_kobj_printf(ops, "\t0x%lx", sp->st_value);
			_kobj_printf(ops, "\t%s\n", name);
		}

#endif
	for (ip = &mp->buckets[kobj_hash_name(name) % mp->hashsize]; *ip;
	    ip = &mp->chains[*ip]) {
		;
	}
	*ip = index;
}

struct modctl *
kobj_boot_mod_lookup(const char *modname)
{
	struct modctl *mctl = kobj_modules;

	do {
		if (strcmp(modname, mctl->mod_modname) == 0)
			return (mctl);
	} while ((mctl = mctl->mod_next) != kobj_modules);

	return (NULL);
}

/*
 * Determine if the module exists.
 */
int
kobj_path_exists(char *name, int use_path)
{
	struct _buf *file;

	file = kobj_open_path(name, use_path, 1);
#ifdef	MODDIR_SUFFIX
	if (file == (struct _buf *)-1)
		file = kobj_open_path(name, use_path, 0);
#endif	/* MODDIR_SUFFIX */
	if (file == (struct _buf *)-1)
		return (0);
	kobj_close_file(file);
	return (1);
}

/*
 * fullname is dynamically allocated to be able to hold the
 * maximum size string that can be constructed from name.
 * path is exactly like the shell PATH variable.
 */
struct _buf *
kobj_open_path(char *name, int use_path, int use_moddir_suffix)
{
	char *p, *q;
	char *pathp;
	char *pathpsave;
	char *fullname;
	int maxpathlen;
	struct _buf *file;

#if !defined(MODDIR_SUFFIX)
	use_moddir_suffix = B_FALSE;
#endif

	if (!use_path)
		pathp = "";		/* use name as specified */
	else
		pathp = kobj_module_path;
					/* use configured default path */

	pathpsave = pathp;		/* keep this for error reporting */

	/*
	 * Allocate enough space for the largest possible fullname.
	 * since path is of the form <directory> : <directory> : ...
	 * we're potentially allocating a little more than we need to
	 * but we'll allocate the exact amount when we find the right directory.
	 * (The + 3 below is one for NULL terminator and one for the '/'
	 * we might have to add at the beginning of path and one for
	 * the '/' between path and name.)
	 */
	maxpathlen = strlen(pathp) + strlen(name) + 3;
	/* sizeof includes null */
	maxpathlen += sizeof (slash_moddir_suffix_slash) - 1;
	fullname = kobj_zalloc(maxpathlen, KM_WAIT);

	for (;;) {
		p = fullname;
		if (*pathp != '\0' && *pathp != '/')
			*p++ = '/';	/* path must start with '/' */
		while (*pathp && *pathp != ':' && *pathp != ' ')
			*p++ = *pathp++;
		if (p != fullname && p[-1] != '/')
			*p++ = '/';
		if (use_moddir_suffix) {
			char *b = basename(name);
			char *s;

			/* copy everything up to the base name */
			q = name;
			while (q != b && *q)
				*p++ = *q++;
			s = slash_moddir_suffix_slash;
			while (*s)
				*p++ = *s++;
			/* copy the rest */
			while (*b)
				*p++ = *b++;
		} else {
			q = name;
			while (*q)
				*p++ = *q++;
		}
		*p = 0;
		if ((file = kobj_open_file(fullname)) != (struct _buf *)-1) {
			kobj_free(fullname, maxpathlen);
			return (file);
		}
		if (*pathp == 0)
			break;
		pathp++;
	}
	kobj_free(fullname, maxpathlen);
	if (_moddebug & MODDEBUG_ERRMSG) {
		_kobj_printf(ops, "can't open %s,", name);
		_kobj_printf(ops, " path is %s\n", pathpsave);
	}
	return ((struct _buf *)-1);
}

intptr_t
kobj_open(char *filename)
{
	struct vnode *vp;
	int fd;

	if (_modrootloaded) {
		struct kobjopen_tctl *ltp = kobjopen_alloc(filename);
		int Errno;

		/*
		 * Hand off the open to a thread who has a
		 * stack size capable handling the request.
		 */
		if (curthread != &t0) {
			(void) thread_create(NULL, DEFAULTSTKSZ * 2,
			    kobjopen_thread, ltp, 0, &p0, TS_RUN, maxclsyspri);
			sema_p(&ltp->sema);
			Errno = ltp->Errno;
			vp = ltp->vp;
		} else {
			/*
			 * 1098067: module creds should not be those of the
			 * caller
			 */
			cred_t *saved_cred = curthread->t_cred;
			curthread->t_cred = kcred;
			Errno = vn_openat(filename, UIO_SYSSPACE, FREAD, 0, &vp,
			    0, 0, rootdir);
			curthread->t_cred = saved_cred;
		}
		kobjopen_free(ltp);

		if (Errno) {
			if (_moddebug & MODDEBUG_ERRMSG) {
				_kobj_printf(ops,
				    "kobj_open: vn_open of %s fails, ",
				    filename);
				_kobj_printf(ops, "Errno = %d\n", Errno);
			}
			return (-1);
		} else {
			if (_moddebug & MODDEBUG_ERRMSG) {
				_kobj_printf(ops, "kobj_open: '%s'", filename);
				_kobj_printf(ops, " vp = %p\n", vp);
			}
			return ((intptr_t)vp);
		}
	} else {
		fd = kobj_boot_open(filename, 0);

		if (_moddebug & MODDEBUG_ERRMSG) {
			if (fd < 0)
				_kobj_printf(ops,
				    "kobj_open: can't open %s\n", filename);
			else {
				_kobj_printf(ops, "kobj_open: '%s'", filename);
				_kobj_printf(ops, " descr = 0x%x\n", fd);
			}
		}
		return ((intptr_t)fd);
	}
}

/*
 * Calls to kobj_open() are handled off to this routine as a separate thread.
 */
static void
kobjopen_thread(struct kobjopen_tctl *ltp)
{
	kmutex_t	cpr_lk;
	callb_cpr_t	cpr_i;

	mutex_init(&cpr_lk, NULL, MUTEX_DEFAULT, NULL);
	CALLB_CPR_INIT(&cpr_i, &cpr_lk, callb_generic_cpr, "kobjopen");
	ltp->Errno = vn_open(ltp->name, UIO_SYSSPACE, FREAD, 0, &(ltp->vp),
	    0, 0);
	sema_v(&ltp->sema);
	mutex_enter(&cpr_lk);
	CALLB_CPR_EXIT(&cpr_i);
	mutex_destroy(&cpr_lk);
	thread_exit();
}

/*
 * allocate and initialize a kobjopen thread structure
 */
static struct kobjopen_tctl *
kobjopen_alloc(char *filename)
{
	struct kobjopen_tctl *ltp = kmem_zalloc(sizeof (*ltp), KM_SLEEP);

	ASSERT(filename != NULL);

	ltp->name = kmem_alloc(strlen(filename) + 1, KM_SLEEP);
	bcopy(filename, ltp->name, strlen(filename) + 1);
	sema_init(&ltp->sema, 0, NULL, SEMA_DEFAULT, NULL);
	return (ltp);
}

/*
 * free a kobjopen thread control structure
 */
static void
kobjopen_free(struct kobjopen_tctl *ltp)
{
	sema_destroy(&ltp->sema);
	kmem_free(ltp->name, strlen(ltp->name) + 1);
	kmem_free(ltp, sizeof (*ltp));
}

int
kobj_read(intptr_t descr, char *buf, unsigned size, unsigned offset)
{
	int stat;
	ssize_t resid;

	if (_modrootloaded) {
		if ((stat = vn_rdwr(UIO_READ, (struct vnode *)descr, buf, size,
		    (offset_t)offset, UIO_SYSSPACE, 0, (rlim64_t)0, CRED(),
		    &resid)) != 0) {
			_kobj_printf(ops,
			    "vn_rdwr failed with error 0x%x\n", stat);
			return (-1);
		}
		return (size - resid);
	} else {
		int count = 0;

		if (kobj_boot_seek((int)descr, (off_t)0, offset) != 0) {
			_kobj_printf(ops,
			    "kobj_read: seek 0x%x failed\n", offset);
			return (-1);
		}

		count = kobj_boot_read((int)descr, buf, size);
		if (count < size) {
			if (_moddebug & MODDEBUG_ERRMSG) {
				_kobj_printf(ops,
				    "kobj_read: req %d bytes, ", size);
				_kobj_printf(ops, "got %d\n", count);
			}
		}
		return (count);
	}
}

void
kobj_close(intptr_t descr)
{
	if (_moddebug & MODDEBUG_ERRMSG)
		_kobj_printf(ops, "kobj_close: 0x%lx\n", descr);

	if (_modrootloaded) {
		struct vnode *vp = (struct vnode *)descr;
		(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED());
		VN_RELE(vp);
	} else
		(void) kobj_boot_close((int)descr);
}

int
kobj_fstat(intptr_t descr, struct bootstat *buf)
{
	if (buf == NULL)
		return (-1);

	if (_modrootloaded) {
		vattr_t vattr;
		struct vnode *vp = (struct vnode *)descr;
		if (VOP_GETATTR(vp, &vattr, 0, kcred) != 0)
			return (-1);

		/*
		 * The vattr and bootstat structures are similar, but not
		 * identical.  We do our best to fill in the bootstat structure
		 * from the contents of vattr (transfering only the ones that
		 * are obvious.
		 */

		buf->st_mode = (uint32_t)vattr.va_mode;
		buf->st_nlink = (uint32_t)vattr.va_nlink;
		buf->st_uid = (int32_t)vattr.va_uid;
		buf->st_gid = (int32_t)vattr.va_gid;
		buf->st_rdev = (uint64_t)vattr.va_rdev;
		buf->st_size = (uint64_t)vattr.va_size;
		buf->st_atim.tv_sec = (int64_t)vattr.va_atime.tv_sec;
		buf->st_atim.tv_nsec = (int64_t)vattr.va_atime.tv_nsec;
		buf->st_mtim.tv_sec = (int64_t)vattr.va_mtime.tv_sec;
		buf->st_mtim.tv_nsec = (int64_t)vattr.va_mtime.tv_nsec;
		buf->st_ctim.tv_sec = (int64_t)vattr.va_ctime.tv_sec;
		buf->st_ctim.tv_nsec = (int64_t)vattr.va_ctime.tv_nsec;
		buf->st_blksize = (int32_t)vattr.va_blksize;
		buf->st_blocks = (int64_t)vattr.va_nblocks;

		return (0);
	}

	return (kobj_boot_fstat((int)descr, buf));
}


struct _buf *
kobj_open_file(char *name)
{
	struct _buf *file;
	intptr_t fd;

	if ((fd = kobj_open(name)) == -1) {
		return ((struct _buf *)-1);
	}

	file = kobj_zalloc(sizeof (struct _buf), KM_WAIT|KM_TMP);
	file->_fd = fd;
	file->_name = kobj_alloc(strlen(name)+1, KM_WAIT|KM_TMP);
	file->_base = kobj_zalloc(MAXBSIZE, KM_WAIT|KM_TMP);
	file->_cnt = file->_size = file->_off = 0;
	file->_ln = 1;
	file->_ptr = file->_base;
	(void) strcpy(file->_name, name);
	return (file);
}

void
kobj_close_file(struct _buf *file)
{
	kobj_close(file->_fd);
	kobj_free(file->_base, MAXBSIZE);
	kobj_free(file->_name, strlen(file->_name)+1);
	kobj_free(file, sizeof (struct _buf));
}

int
kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off)
{
	int b_size, c_size;
	int b_off;	/* Offset into buffer for start of bcopy */
	int count = 0;
	int page_addr;

	if (_moddebug & MODDEBUG_ERRMSG) {
		_kobj_printf(ops, "kobj_read_file: size=%x,", size);
		_kobj_printf(ops, " offset=%x at", off);
		_kobj_printf(ops, " buf=%x\n", buf);
	}

	while (size) {
		page_addr = F_PAGE(off);
		b_size = file->_size;
		/*
		 * If we have the filesystem page the caller's referring to
		 * and we have something in the buffer,
		 * satisfy as much of the request from the buffer as we can.
		 */
		if (page_addr == file->_off && b_size > 0) {
			b_off = B_OFFSET(off);
			c_size = b_size - b_off;
			/*
			 * If there's nothing to copy, we're at EOF.
			 */
			if (c_size <= 0)
				break;
			if (c_size > size)
				c_size = size;
			if (buf) {
				if (_moddebug & MODDEBUG_ERRMSG)
					_kobj_printf(ops, "copying %x bytes\n",
					    c_size);
				bcopy(file->_base+b_off, buf, c_size);
				size -= c_size;
				off += c_size;
				buf += c_size;
				count += c_size;
			} else {
				_kobj_printf(ops, "kobj_read: system error");
				count = -1;
				break;
			}
		} else {
			/*
			 * If the caller's offset is page aligned and
			 * the caller want's at least a filesystem page and
			 * the caller provided a buffer,
			 * read directly into the caller's buffer.
			 */
			if (page_addr == off &&
			    (c_size = F_PAGE(size)) && buf) {
				c_size = kobj_read(file->_fd, buf, c_size,
				    page_addr);
				if (c_size < 0) {
					count = -1;
					break;
				}
				count += c_size;
				if (c_size != F_PAGE(size))
					break;
				size -= c_size;
				off += c_size;
				buf += c_size;
			/*
			 * Otherwise, read into our buffer and copy next time
			 * around the loop.
			 */
			} else {
				file->_off = page_addr;
				c_size = kobj_read(file->_fd, file->_base,
				    MAXBSIZE, page_addr);
				file->_ptr = file->_base;
				file->_cnt = c_size;
				file->_size = c_size;
				/*
				 * If a _filbuf call or nothing read, break.
				 */
				if (buf == NULL || c_size <= 0) {
					count = c_size;
					break;
				}
			}
			if (_moddebug & MODDEBUG_ERRMSG)
				_kobj_printf(ops, "read %x bytes\n", c_size);
		}
	}
	if (_moddebug & MODDEBUG_ERRMSG)
		_kobj_printf(ops, "count = %x\n", count);

	return (count);
}

int
kobj_filbuf(struct _buf *f)
{
	if (kobj_read_file(f, NULL, MAXBSIZE, f->_off + f->_size) > 0)
		return (kobj_getc(f));
	return (-1);
}

void
kobj_free(void *address, size_t size)
{
	if (standalone)
		return;

	kmem_free(address, size);
	kobj_stat.nfree_calls++;
	kobj_stat.nfree += size;
}

void *
kobj_zalloc(size_t size, int flag)
{
	void *v;

	if ((v = kobj_alloc(size, flag)) != 0) {
		bzero(v, size);
	}

	return (v);
}

void *
kobj_alloc(size_t size, int flag)
{
	/*
	 * If we are running standalone in the
	 * linker, we ask boot for memory.
	 * Either it's temporary memory that we lose
	 * once boot is mapped out or we allocate it
	 * permanently using the dynamic data segment.
	 */
	if (standalone) {
#ifdef __sparc
		if (flag & KM_TMP) {
			return (kobj_tmp_alloc(size));
		} else if (flag & KM_SCRATCH) {
			void *buf = kobj_bs_alloc(size);

			if (buf != NULL)
				return (buf);
#ifdef	KOBJ_DEBUG
			if (kobj_debug & D_DEBUG) {
				_kobj_printf(ops, "krtld: failed scratch alloc "
				    "of %lu bytes -- falling back\n", size);
			}
#endif
		}

#else /* x86 */
		if (flag & (KM_TMP | KM_SCRATCH))
			return (BOP_ALLOC(ops, 0, size, MINALIGN));
#endif
		return (kobj_segbrk(&_edata, size, MINALIGN, 0));
	}

	kobj_stat.nalloc_calls++;
	kobj_stat.nalloc += size;

	return (kmem_alloc(size, (flag & KM_NOWAIT) ? KM_NOSLEEP : KM_SLEEP));
}

/*
 * Allow the "mod" system to sync up with the work
 * already done by kobj during the initial loading
 * of the kernel.  This also gives us a chance
 * to reallocate memory that belongs to boot.
 */
void
kobj_sync(void)
{
	struct modctl_list *lp, **lpp;

	/*
	 * The module path can be set in /etc/system via 'moddir' commands
	 */
	if (default_path != NULL)
		kobj_module_path = default_path;
	else
		default_path = kobj_module_path;

	ksyms_arena = vmem_create("ksyms", NULL, 0, sizeof (uint64_t),
	    segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP);

	ctf_arena = vmem_create("ctf", NULL, 0, sizeof (uint_t),
	    segkmem_alloc, segkmem_free, heap_arena, 0, VM_SLEEP);

	/*
	 * Move symbol tables from boot memory to ksyms_arena.
	 */
	for (lpp = kobj_linkmaps; *lpp != NULL; lpp++) {
		for (lp = *lpp; lp != NULL; lp = lp->modl_next)
			kobj_export_module(mod(lp));
	}
}

caddr_t
kobj_segbrk(caddr_t *spp, size_t size, size_t align, caddr_t limit)
{
	uintptr_t va, pva;
	size_t alloc_pgsz = kobj_mmu_pagesize;
	size_t alloc_align = BO_NO_ALIGN;
	size_t alloc_size;

	/*
	 * If we are using "large" mappings for the kernel,
	 * request aligned memory from boot using the
	 * "large" pagesize.
	 */
	if (lg_pagesize) {
		alloc_align = lg_pagesize;
		alloc_pgsz = lg_pagesize;
	}
	va = ALIGN((uintptr_t)*spp, align);
	pva = P2ROUNDUP((uintptr_t)*spp, alloc_pgsz);
	/*
	 * Need more pages?
	 */
	if (va + size > pva) {
		uintptr_t npva;

		alloc_size = P2ROUNDUP(size - (pva - va), alloc_pgsz);
		/*
		 * Check for overlapping segments.
		 */
		if (limit && limit <= *spp + alloc_size) {
			return ((caddr_t)0);
		}

		npva = (uintptr_t)BOP_ALLOC(ops, (caddr_t)pva,
		    alloc_size, alloc_align);

		if (npva == NULL) {
			_kobj_printf(ops, "BOP_ALLOC failed, 0x%lx bytes",
			    alloc_size);
			_kobj_printf(ops, " aligned %lx", alloc_align);
			_kobj_printf(ops, " at 0x%lx\n", pva);
			return (NULL);
		}
	}
	*spp = (caddr_t)(va + size);

	return ((caddr_t)va);
}

/*
 * Calculate the number of output hash buckets.
 * We use the next prime larger than n / 4,
 * so the average hash chain is about 4 entries.
 * More buckets would just be a waste of memory.
 */
uint_t
kobj_gethashsize(uint_t n)
{
	int f;
	int hsize = MAX(n / 4, 2);

	for (f = 2; f * f <= hsize; f++)
		if (hsize % f == 0)
			hsize += f = 1;

	return (hsize);
}

/*
 * Get the file size.
 *
 * Before root is mounted, files are compressed in the boot_archive ramdisk
 * (in the memory). kobj_fstat would return the compressed file size.
 * In order to get the uncompressed file size, read the file to the end and
 * count its size.
 */
int
kobj_get_filesize(struct _buf *file, uint64_t *size)
{
	if (_modrootloaded) {
		struct bootstat bst;

		if (kobj_fstat(file->_fd, &bst) != 0)
			return (EIO);
		*size = bst.st_size;
	} else {
		char *buf;
		int count;
		uint64_t offset = 0;

		buf = kmem_alloc(MAXBSIZE, KM_SLEEP);
		do {
			count = kobj_read_file(file, buf, MAXBSIZE, offset);
			if (count < 0) {
				kmem_free(buf, MAXBSIZE);
				return (EIO);
			}
			offset += count;
		} while (count == MAXBSIZE);
		kmem_free(buf, MAXBSIZE);

		*size = offset;
	}

	return (0);
}

static char *
basename(char *s)
{
	char *p, *q;

	q = NULL;
	p = s;
	do {
		if (*p == '/')
			q = p;
	} while (*p++);
	return (q ? q + 1 : s);
}

/*ARGSUSED*/
static void
kprintf(void *op, const char *fmt, ...)
{
	va_list adx;

	va_start(adx, fmt);
	vprintf(fmt, adx);
	va_end(adx);
}

void
kobj_stat_get(kobj_stat_t *kp)
{
	*kp = kobj_stat;
}

int
kobj_getpagesize()
{
	return (lg_pagesize);
}

void
kobj_textwin_alloc(struct module *mp)
{
	ASSERT(MUTEX_HELD(&mod_lock));

	if (mp->textwin != NULL)
		return;

	/*
	 * If the text is not contained in the heap, then it is not contained
	 * by a writable mapping.  (Specifically, it's on the nucleus page.)
	 * We allocate a read/write mapping for this module's text to allow
	 * the text to be patched without calling hot_patch_kernel_text()
	 * (which is quite slow).
	 */
	if (!vmem_contains(heaptext_arena, mp->text, mp->text_size)) {
		uintptr_t text = (uintptr_t)mp->text;
		uintptr_t size = (uintptr_t)mp->text_size;
		uintptr_t i;
		caddr_t va;
		size_t sz = ((text + size + PAGESIZE - 1) & PAGEMASK) -
		    (text & PAGEMASK);

		va = mp->textwin_base = vmem_alloc(heap_arena, sz, VM_SLEEP);

		for (i = text & PAGEMASK; i < text + size; i += PAGESIZE) {
			hat_devload(kas.a_hat, va, PAGESIZE,
			    hat_getpfnum(kas.a_hat, (caddr_t)i),
			    PROT_READ | PROT_WRITE,
			    HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
			va += PAGESIZE;
		}

		mp->textwin = mp->textwin_base + (text & PAGEOFFSET);
	} else {
		mp->textwin = mp->text;
	}
}

void
kobj_textwin_free(struct module *mp)
{
	uintptr_t text = (uintptr_t)mp->text;
	uintptr_t tsize = (uintptr_t)mp->text_size;
	size_t size = (((text + tsize + PAGESIZE - 1) & PAGEMASK) -
	    (text & PAGEMASK));

	mp->textwin = NULL;

	if (mp->textwin_base == NULL)
		return;

	hat_unload(kas.a_hat, mp->textwin_base, size, HAT_UNLOAD_UNLOCK);
	vmem_free(heap_arena, mp->textwin_base, size);
	mp->textwin_base = NULL;
}

static char *
find_libmacro(char *name)
{
	int lmi;

	for (lmi = 0; lmi < NLIBMACROS; lmi++) {
		if (strcmp(name, libmacros[lmi].lmi_macroname) == 0)
			return (libmacros[lmi].lmi_list);
	}
	return (NULL);
}

/*
 * Check for $MACRO in tail (string to expand) and expand it in path at pathend
 * returns path if successful, else NULL
 * Support multiple $MACROs expansion and the first valid path will be returned
 * Caller's responsibility to provide enough space in path to expand
 */
char *
expand_libmacro(char *tail, char *path, char *pathend)
{
	char c, *p, *p1, *p2, *path2, *endp;
	int diff, lmi, macrolen, valid_macro, more_macro;
	struct _buf *file;

	/*
	 * check for $MACROS between nulls or slashes
	 */
	p = strchr(tail, '$');
	if (p == NULL)
		return (NULL);
	for (lmi = 0; lmi < NLIBMACROS; lmi++) {
		macrolen = libmacros[lmi].lmi_macrolen;
		if (strncmp(p + 1, libmacros[lmi].lmi_macroname, macrolen) == 0)
			break;
	}

	valid_macro = 0;
	if (lmi < NLIBMACROS) {
		/*
		 * The following checks are used to restrict expansion of
		 * macros to those that form a full directory/file name
		 * and to keep the behavior same as before.  If this
		 * restriction is removed or no longer valid in the future,
		 * the checks below can be deleted.
		 */
		if ((p == tail) || (*(p - 1) == '/')) {
			c = *(p + macrolen + 1);
			if (c == '/' || c == '\0')
				valid_macro = 1;
		}
	}

	if (!valid_macro) {
		p2 = strchr(p, '/');
		/*
		 * if no more macro to expand, then just copy whatever left
		 * and check whether it exists
		 */
		if (p2 == NULL || strchr(p2, '$') == NULL) {
			(void) strcpy(pathend, tail);
			if ((file = kobj_open_path(path, 1, 1)) !=
			    (struct _buf *)-1) {
				kobj_close_file(file);
				return (path);
			} else
				return (NULL);
		} else {
			/*
			 * copy all chars before '/' and call expand_libmacro()
			 * again
			 */
			diff = p2 - tail;
			bcopy(tail, pathend, diff);
			pathend += diff;
			*(pathend) = '\0';
			return (expand_libmacro(p2, path, pathend));
		}
	}

	more_macro = 0;
	if (c != '\0') {
		endp = p + macrolen + 1;
		if (strchr(endp, '$') != NULL)
			more_macro = 1;
	} else
		endp = NULL;

	/*
	 * copy lmi_list and split it into components.
	 * then put the part of tail before $MACRO into path
	 * at pathend
	 */
	diff = p - tail;
	if (diff > 0)
		bcopy(tail, pathend, diff);
	path2 = pathend + diff;
	p1 = libmacros[lmi].lmi_list;
	while (p1 && (*p1 != '\0')) {
		p2 = strchr(p1, ':');
		if (p2) {
			diff = p2 - p1;
			bcopy(p1, path2, diff);
			*(path2 + diff) = '\0';
		} else {
			diff = strlen(p1);
			bcopy(p1, path2, diff + 1);
		}
		/* copy endp only if there isn't any more macro to expand */
		if (!more_macro && (endp != NULL))
			(void) strcat(path2, endp);
		file = kobj_open_path(path, 1, 1);
		if (file != (struct _buf *)-1) {
			kobj_close_file(file);
			/*
			 * if more macros to expand then call expand_libmacro(),
			 * else return path which has the whole path
			 */
			if (!more_macro || (expand_libmacro(endp, path,
			    path2 + diff) != NULL)) {
				return (path);
			}
		}
		if (p2)
			p1 = ++p2;
		else
			return (NULL);
	}
	return (NULL);
}

static void
tnf_add_notifyunload(kobj_notify_f *fp)
{
	kobj_notify_list_t *entry;

	entry = kobj_alloc(sizeof (kobj_notify_list_t), KM_WAIT);
	entry->kn_type = KOBJ_NOTIFY_MODUNLOADING;
	entry->kn_func = fp;
	(void) kobj_notify_add(entry);
}

/* ARGSUSED */
static void
tnf_unsplice_probes(unsigned int what, struct modctl *mod)
{
	extern tnf_probe_control_t *__tnf_probe_list_head;
	extern tnf_tag_data_t *__tnf_tag_list_head;
	tnf_probe_control_t **p;
	tnf_tag_data_t **q;
	struct module *mp = mod->mod_mp;

	if (!(mp->flags & KOBJ_TNF_PROBE))
		return;

	for (p = &__tnf_probe_list_head; *p; )
		if (kobj_addrcheck(mp, (char *)*p) == 0)
			*p = (*p)->next;
		else
			p = &(*p)->next;

	for (q = &__tnf_tag_list_head; *q; )
		if (kobj_addrcheck(mp, (char *)*q) == 0)
			*q = (tnf_tag_data_t *)(*q)->tag_version;
		else
			q = (tnf_tag_data_t **)&(*q)->tag_version;

	tnf_changed_probe_list = 1;
}

int
tnf_splice_probes(int boot_load, tnf_probe_control_t *plist,
    tnf_tag_data_t *tlist)
{
	int result = 0;
	static int add_notify = 1;

	if (plist) {
		tnf_probe_control_t *pl;

		for (pl = plist; pl->next; )
			pl = pl->next;

		if (!boot_load)
			mutex_enter(&mod_lock);
		tnf_changed_probe_list = 1;
		pl->next = __tnf_probe_list_head;
		__tnf_probe_list_head = plist;
		if (!boot_load)
			mutex_exit(&mod_lock);
		result = 1;
	}

	if (tlist) {
		tnf_tag_data_t *tl;

		for (tl = tlist; tl->tag_version; )
			tl = (tnf_tag_data_t *)tl->tag_version;

		if (!boot_load)
			mutex_enter(&mod_lock);
		tl->tag_version = (tnf_tag_version_t *)__tnf_tag_list_head;
		__tnf_tag_list_head = tlist;
		if (!boot_load)
			mutex_exit(&mod_lock);
		result = 1;
	}
	if (!boot_load && result && add_notify) {
		tnf_add_notifyunload(tnf_unsplice_probes);
		add_notify = 0;
	}
	return (result);
}

#if defined(__x86)
/*
 * This code is for the purpose of manually recording which files
 * needs to go into the boot archive on any given system.
 *
 * To enable the code, set kobj_file_bufsize in /etc/system
 * and reboot the system, then use mdb to look at kobj_file_buf.
 */
static void
kobj_record_file(char *filename)
{
	extern char *kobj_file_buf;
	extern int kobj_file_bufsize;
	static char *buf;
	static int size = 0;
	int n;

	if (standalone)		/* kernel symbol not available */
		return;

	if (kobj_file_bufsize == 0)	/* don't bother */
		return;

	if (kobj_file_buf == NULL) {	/* allocate buffer */
		size = kobj_file_bufsize;
		buf = kobj_file_buf = kobj_alloc(size, KM_WAIT|KM_TMP);
	}

	n = snprintf(buf, size, "%s\n", filename);
	if (n > size)
		n = size;
	size -= n;
	buf += n;
}
#endif	/* __x86 */

static int
kobj_boot_fstat(int fd, struct bootstat *stp)
{
#if defined(__sparc)
	if (!standalone && _ioquiesced)
		return (-1);
	return (BOP_FSTAT(ops, fd, stp));
#else
	return (BRD_FSTAT(bfs_ops, fd, stp));
#endif
}

/*
 * XXX these wrappers should go away when sparc is converted
 * boot from ramdisk
 */
static int
kobj_boot_open(char *filename, int flags)
{
#if defined(__sparc)
	/*
	 * If io via bootops is quiesced, it means boot is no longer
	 * available to us.  We make it look as if we can't open the
	 * named file - which is reasonably accurate.
	 */
	if (!standalone && _ioquiesced)
		return (-1);

	return (BOP_OPEN(ops, filename, flags));
#else /* x86 */
	kobj_record_file(filename);
	return (BRD_OPEN(bfs_ops, filename, flags));
#endif
}

static int
kobj_boot_close(int fd)
{
#if defined(__sparc)
	if (!standalone && _ioquiesced)
		return (-1);

	return (BOP_CLOSE(ops, fd));
#else /* x86 */
	return (BRD_CLOSE(bfs_ops, fd));
#endif
}

/*ARGSUSED*/
static int
kobj_boot_seek(int fd, off_t hi, off_t lo)
{
#if defined(__sparc)
	return (BOP_SEEK(ops, fd, hi, lo));
#else
	return (BRD_SEEK(bfs_ops, fd, lo, SEEK_SET));
#endif
}

static int
kobj_boot_read(int fd, caddr_t buf, size_t size)
{
#if defined(__sparc)
	return (BOP_READ(ops, fd, buf, size));
#else
	return (BRD_READ(bfs_ops, fd, buf, size));
#endif
}