/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2011 Bayard G. Bell <buffer.g.overflow@gmail.com>.
* All rights reserved. Use is subject to license terms.
*/
/*
*/
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
#include <vm/seg_kmem.h>
#include <sys/elf_notes.h>
#include <sys/kobj_impl.h>
#include <sys/tnf_probe.h>
#include <krtld/kobj_kdi.h>
#if !defined(_OBP)
#endif
/*
* do_symbols() error codes
*/
#if !defined(_OBP)
static void synthetic_bootaux(char *, val_t *);
#endif
static void load_linker(val_t *);
static int bind_primary(val_t *, int);
static int load_primary(struct module *, int);
static void free_module_data(struct module *);
static char *depends_on(struct module *);
static char *getmodpath(const char *);
static char *basename(char *);
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_fstat(int, struct bootstat *);
static int kobj_boot_compinfo(int, struct compinfo *);
static int kobj_is_compressed(intptr_t);
extern int elf_mach_ok(Ehdr *);
#if !defined(_OBP)
extern int kobj_boot_mountroot(void);
#endif
extern tnf_probe_control_t *__tnf_probe_list_head;
extern tnf_tag_data_t *__tnf_tag_list_head;
extern int modrootloaded;
extern int swaploaded;
extern int bop_io_quiesced;
extern int last_module_id;
extern char stubs_base[];
extern char stubs_end[];
#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;
#else
#endif
#ifdef MODDIR_SUFFIX
#else
#endif
/*
* kobjopen thread control structure
*/
struct kobjopen_tctl {
};
/*
* Structure for defining dynamically expandable library macros
*/
struct lib_macro_info {
} libmacros[] = {
};
/*
* 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
* indicating that these symbols have been multiply 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_* */
/*
* TNF probe management globals
*/
int tnf_changed_probe_list = 0;
/*
* Prefix for statically defined tracing (SDT) DTrace probes.
*/
/*
*/
/*
* The sparc linker doesn't create a memory location
* for a variable named _edata, so _edata can only be
* referred to, not modified. krtld needs a static
* variable to modify it - within krtld, of course -
* outside of krtld, e_data is used in all kernels.
*/
#if defined(__sparc)
#else
#endif
/*
* _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.
*/
/*
* Standalone function pointers for use within krtld.
* Many platforms implement optimized platmod versions of
* utilities such as bcopy and any such are not yet available
* until the kernel is more completely stitched together.
* See kobj_impl.h
*/
int
{
if (standalone)
return (0);
}
static void *
{
if (standalone)
return (0);
}
/*
* 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
void *romvec,
void *dvec,
{
/*
* Save these to pass on to
* the booted standalone.
*/
KOBJ_MARK("Entered kobj_init()");
/*
* 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.
*/
"kadb are no longer supported\n\n");
goto fail;
}
#if defined(_OBP)
/*
* OBP allows us to read both the ramdisk and
* the underlying root fs when root is a disk.
* This can lower incidences of unbootable systems
* when the archive is out-of-date with the /etc
* state files.
*/
if (BOP_MOUNTROOT() != BOOT_SVC_OK) {
goto fail;
}
#else
{
/* on x86, we always boot with a ramdisk */
(void) kobj_boot_mountroot();
/*
* Now that the ramdisk is mounted, finish boot property
* initialization.
*/
}
#if !defined(_UNIX_KRTLD)
/*
* 'unix' is linked together with 'krtld' into one executable and
* 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.
*
* OBP tells kobj_start() where the ELF image is in memory, so it
* synthesized bootaux before kobj_init() was called
*/
#endif /* !_UNIX_KRTLD */
#endif /* _OBP */
/*
* Save the interesting attribute-values
* (scanned by kobj_boot).
*/
/*
* Set the module search path.
*/
/*
* These two modules have actually been
* loaded by boot, but we finish the job
* by introducing them into the world of
* loadable modules.
*/
/*
* Load all the primary dependent modules.
*/
goto fail;
/*
* Glue it together.
*/
goto fail;
/*
* Get the boot flags
*/
if (boothowto & RB_VERBOSE)
goto fail;
}
/*
* Post setup.
*/
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG)
"krtld: transferring control to: 0x%p\n", entry);
#endif
/*
* Make sure the mod system knows about the modules already loaded.
*/
do {
standalone = 0;
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG)
"krtld: really transferring control to: 0x%p\n", entry);
#endif
#if defined(_DBOOT)
/*
* krtld was called from a dboot ELF section, the embedded
* dboot code contains the real entry via bootaux
*/
#else
/*
* krtld was directly called from startup
*/
return;
#endif
fail:
#if !defined(_UNIX_KRTLD)
" for:\n");
" file\n");
" fix this.\n");
bop_panic("Unable to boot");
#endif
}
#if !defined(_UNIX_KRTLD) && !defined(_OBP)
/*
* Synthesize additional metadata that describes the executable if
* krtld's caller didn't do it.
*
* (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
{
int i, n;
/*
* Elf header
*/
KOBJ_MARK("synthetic_bootaux()");
return;
}
KOBJ_MARK("reading program headers");
filename);
return;
}
/*
* Program headers
*/
filename);
return;
}
KOBJ_MARK("closed file");
/*
* Find the dynamic section address
*/
break;
}
}
KOBJ_MARK("synthetic_bootaux() done");
}
#endif /* !_UNIX_KRTLD && !_OBP */
/*
* Set up any global information derived
* aux vector.
*/
static void
{
int i;
KOBJ_MARK("attr_val()");
for (i = 0; i < phnum; i++) {
continue;
}
/*
* Bounds of the various segments.
*/
#if defined(_RELSEG)
/*
* sparc kernel puts the dynamic info
* into a separate segment, which is
* free'd in bop_fini()
*/
#else
#endif
} else {
} else {
}
}
}
/* To do the kobj_alloc, _edata needs to be set. */
for (i = 0; i < NLIBMACROS; i++) {
1, KM_WAIT);
}
}
}
/*
* Set up the booted executable.
*/
static struct module *
{
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG)
#endif
KOBJ_MARK("add_primary");
KOBJ_MARK("struct module");
/*
* We don't have the following information
* since this module is an executable and not
* a relocatable .o.
*/
mp->symtbl_section = 0;
/*
* Since this module is the only exception,
* we cons up some section headers.
*/
KOBJ_MARK("symhdr");
KOBJ_MARK("strhdr");
/*
* Scan the dynamic structure.
*/
case DT_SYMTAB:
break;
case DT_HASH:
break;
case DT_STRTAB:
break;
case DT_STRSZ:
break;
case DT_SYMENT:
break;
}
}
/*
* Collapse any DT_NEEDED entries into one string.
*/
KOBJ_MARK("depends_on");
char *_lib;
else
"load_exec: fail to "
"expand %s\n", libname);
}
KOBJ_MARK("grow depends_on");
KM_WAIT);
allocsize += MAXPATHLEN;
}
nsize++;
}
if (nsize) {
/*
* alloc with exact size and copy whatever it got over
*/
KOBJ_MARK("realloc depends_on");
} else {
}
/*
* We allocate our own table since we don't
* hash undefined references.
*/
KOBJ_MARK("chains");
KOBJ_MARK("buckets");
#ifdef KOBJ_DEBUG
if (kobj_debug & D_LOADING) {
}
#endif /* KOBJ_DEBUG */
/*
* Insert symbols into the hash table.
*/
continue;
#if defined(__sparc)
/*
* Register symbols are ignored in the kernel
*/
continue;
#endif /* __sparc */
}
KOBJ_MARK("load_exec done");
return (mp);
}
/*
* Set up the linker module (if it's compiled in, LDNAME is NULL)
*/
static void
{
int i;
int shsize;
/*
* On some architectures, krtld is compiled into the kernel.
*/
return;
}
}
mp->symtbl_section = i;
}
}
/*
* Now that we've figured out where the linker is,
* set the limits for the booted object.
*/
#ifdef KOBJ_DEBUG
if (kobj_debug & D_LOADING) {
}
#endif /* KOBJ_DEBUG */
/*
* Insert the symbols into the hash table.
*/
continue;
}
}
}
static kobj_notify_list_t **
{
sizeof (kobj_notify_list_t *));
return (&kobj_notifiers[type]);
}
int
{
}
return (0);
}
int
{
/* LINTED */
/* LINTED */
else
return (0);
}
/*
* Notify all interested callbacks of a specified change in module state.
*/
static void
{
return;
/*
* KDI notification must be last (it has to allow for work done by the
* other notification callbacks), so we call it manually.
*/
}
/*
* Create the module path.
*/
static char *
{
/*
* Platform code gets first crack, then add
* the default components
*/
if (*path != '\0')
}
static struct modctl *
{
/*
* For symbol lookup, we assemble our own
* modctl list of the primary modules.
*/
/* set values for modinfo assuming that the load will work */
/*
* Link the module in. We'll pass this info on
* to the mod squad later.
*/
if (kobj_modules == NULL) {
kobj_modules = cp;
} else {
}
return (cp);
}
static int
{
/*
* Do common symbols.
*/
/*
* Don't do common section relocations for modules that
* don't need it.
*/
continue;
return (-1);
}
/*
* Resolve symbols.
*/
if (do_symbols(mp, 0) < 0)
return (-1);
}
/*
* Do relocations.
*/
case DT_RELASZ:
case DT_RELSZ:
break;
case DT_RELAENT:
case DT_RELENT:
break;
case DT_RELA:
break;
case DT_REL:
break;
}
}
if (relasz == 0 ||
"no relocation information found for "
return (-1);
}
#ifdef KOBJ_DEBUG
if (kobj_debug & D_RELOCATIONS)
#endif
return (-1);
} else {
if (do_relocations(mp) < 0)
return (-1);
}
}
/*
* 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).
*/
int n;
return (-1);
return (-1);
return (-1);
return (-1);
return (-1);
/*
* The interpreter path fragment in mp->filename
* will already have the module directory suffix
* in it (if appropriate).
*/
return (-1);
return (-1);
}
}
return (0);
}
static struct modctl *
{
do {
return (mctl);
} while (mctl != kobj_modules);
return (NULL);
}
/*
* Load all the primary dependent modules.
*/
static int
{
char *p, *q;
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.
*/
continue;
/*
* Load it.
*/
cp->mod_loaded = 0;
cp->mod_installed = 0;
cp->mod_loadcnt = 0;
return (-1);
}
/*
* Recurse.
*/
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;
return (0);
return (ret);
}
static int
{
if (console_is_usb_serial()) {
"(unsupported on usb serial console)\n");
return (0);
}
return (-1);
return (-1);
return (-1);
if (boothowto & RB_VERBOSE)
return (-1);
return (-1);
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG) {
}
#endif
(const char **)kobj_kmdb_argv) < 0)
return (-1);
return (0);
}
/*
* Return a string listing module dependencies.
*/
static char *
{
char *depstr, *q;
/*
* The module doesn't have a depends_on value, so let's try it the
* old-fashioned way - via "_depends_on"
*/
return (NULL);
#ifdef KOBJ_DEBUG
/*
* _depends_on is a deprecated interface, so we warn about its use
* irrespective of subsequent processing errors. How else are we going
* to be able to deco this interface completely?
* Changes initially limited to DEBUG because third-party modules
* should be flagged to developers before general use base.
*/
"Warning: %s uses deprecated _depends_on interface.\n",
#endif
/*
* Idiot checks. Make sure it's
* in-bounds and NULL terminated.
*/
return (NULL);
}
return (depstr);
}
void
{
}
/*
* kobj_export_ksyms() performs the following services:
*
* (2) Removes unneeded symbols to save space.
* (3) Reduces memory footprint by using VM_BESTFIT allocations.
*/
static void
{
char *name;
/*
* Make a copy of the original module structure.
*/
/*
* Compute the sizes of the new symbol table sections.
*/
continue;
continue;
locals++;
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.
*/
/*
* Allocate space for the new section headers (symtab and strtab),
* symbol table, buckets, chains, and strings.
*/
} else {
VM_BESTFIT | VM_SLEEP);
}
/*
* Divvy up symspace.
*/
/*
* Fill in the new section headers (symtab and strtab).
*/
mp->symtbl_section = 0;
/*
* Construct the new symbol table.
*/
continue;
continue;
nsyms++;
}
/*
* Free the old section headers -- we'll never need them again.
*/
case SHT_RELA:
case SHT_REL:
}
break;
}
}
}
/*
* Discard the old symbol table and our copy of the module strucure.
*/
}
static void
{
if (_moddebug & MODDEBUG_NOCTF) {
} else {
VM_BESTFIT | VM_SLEEP);
}
}
}
void
{
}
static int
{
int lsize;
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.
*/
char *_lib;
else {
"process_dynamic: failed to expand "
"%s\n", libname);
}
}
KM_WAIT);
}
allocsize += MAXPATHLEN;
}
nsize++;
break;
case DT_FLAGS_1:
break;
}
}
/*
* finish up the depends string (if any)
*/
}
return (0);
}
static int
{
/* find and validate the dynamic section (if any) */
case SHT_DYNAMIC:
"multiple dynamic sections\n");
return (-1);
} else {
}
break;
}
}
return (0);
return (-1);
}
dshn);
return (-1);
}
/* read it from disk */
return (-1);
}
return (-1);
}
/* pull the interesting pieces out */
return (rc);
}
void
{
if (!standalone) {
} else {
}
}
}
/*
* 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.
*/
}
int
{
int i;
int n;
#ifdef MODDIR_SUFFIX
int no_suffixdir_drv = 0;
#endif
/*
* 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.
*/
#ifdef MODDIR_SUFFIX
#endif
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.
*/
no_suffixdir_drv = 1;
}
#endif
}
modname);
goto bad;
}
for (i = 0; i < SELFMAG; i++) {
if (_moddebug & MODDEBUG_ERRMSG)
modname);
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 (_moddebug & MODDEBUG_ERRMSG)
modname);
#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) {
" 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 (_moddebug & MODDEBUG_ERRMSG)
"module\n", modname);
goto bad;
}
"section headers\n", modname);
goto bad;
}
/* read in sections */
goto bad;
}
modname);
goto bad;
}
/* read in symbols; adjust values for each section's real address */
modname);
goto bad;
}
/*
* If we didn't dependency information from the dynamic section, look
* for it the old-fashioned way.
*/
"be available\n", modname);
}
/* primary kernel modules do not have a signature section */
#ifdef KOBJ_DEBUG
if (kobj_debug & D_LOADING) {
}
#endif /* KOBJ_DEBUG */
/*
* For primary kernel modules, we defer
* symbol resolution and relocation until
* all primary objects have been loaded.
*/
if (!standalone) {
char *dependent_modname;
/* load all dependents */
/*
* resolve undefined and common symbols,
* also allocates common space
*/
switch (dcrval) {
case DOSYM_UNSAFE:
"MT-unsafe module '%s' rejected\n",
modname);
break;
case DOSYM_UNDEF:
"cannot load module '%s'\n",
modname);
if (ddrval == -1) {
modname);
"unable to resolve dependency, "
"module '%s' not found\n",
}
break;
}
}
if (dcrval < 0)
goto bad;
/* process relocation tables */
if (do_relocations(mp) < 0) {
modname);
goto bad;
}
if (mp->destination) {
}
/* sync_instruction_memory */
}
return (0);
bad:
}
int
{
if (kobj_load_module(modp, 0) != 0)
return (-1);
/* Bind new module to its dependents */
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG) {
}
#endif
return (-1);
}
/*
* Relocate it. This module may not be part of a link map, so we
* can't use bind_primary.
*/
do_relocations(mp) < 0) {
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG) {
}
#endif
return (-1);
}
return (0);
}
static void
{
if (standalone) {
return;
}
cp->mod_gencount++;
}
void
{
}
/*
* Null out mod_mp first, so consumers (debuggers) know not to look
* at the module structure any more.
*/
}
static void
{
int ksyms_exported = 0;
while (lp) {
}
ksyms_exported = 1;
} else {
ksyms_exported = 1;
}
}
else
}
/*
* We did not get far enough into kobj_export_ksyms() to free allocated
* buffers because we encounted error conditions. Free the buffers.
*/
case SHT_RELA:
case SHT_REL:
break;
}
}
}
}
if (mp->textwin_base)
}
}
if (mp->depends_on)
}
static int
{
/*
* loop through sections to find out how much space we need
* for text, data, (also bss that is already assigned)
*/
goto done;
if (standalone) {
/*
* If we can't grow the text segment, try the
* data segment before failing.
*/
}
goto done;
} else {
if (text_arena == NULL)
/*
* 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.
*/
/*
* a remap is taking place. Align the text ptr relative
* to the secondary mapping. That is where the bits will
* be read in.
*/
while (pages--) {
}
/*
* 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.
*/
}
VM_SLEEP | VM_BESTFIT);
}
/*
* This is the case where a remap is not being done.
*/
if (text == 0)
/* now loop though sections assigning addresses and loading the data */
continue;
else
/*
* Zero bss.
*/
} else {
goto done;
}
} else {
}
else
}
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) {
}
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
{
int i;
for (i = 0; suppress_sym_list[i] != NULL; i++) {
return (1);
}
return (0);
}
static int
{
uint_t i;
char *symname;
int dosymtab = 0;
/*
* Find the interesting sections.
*/
case SHT_SYMTAB:
dosymtab++;
break;
case SHT_RELA:
case SHT_REL:
/*
* Already loaded.
*/
continue;
/* KM_TMP since kobj_free'd in do_relocations */
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) {
return (0);
return (-1);
}
/*
* get the associated string table header
*/
return (-1);
/*
* Allocate space for the symbol table, buckets, chains, and strings.
*/
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.
*/
return (-1);
}
}
continue;
return (-1);
/*
* 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 ||
"%s multiply defined\n", symname);
}
}
}
}
return (0);
}
static int
{
uint_t i;
if (_moddebug & MODDEBUG_NOCTF)
return (0); /* do not attempt to even load CTF data */
return (-1);
}
return (-1);
}
return (-1);
}
break;
}
}
return (0);
}
/*
* 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
{
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.
*/
continue;
#ifdef KOBJ_DEBUG
if (kobj_debug & D_DEBUG)
"krtld: crypto_es_hash: updating hash with"
#endif
}
}
/*
* 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
{
uint_t i;
return;
}
return;
}
ELF_SIGNATURE_SECTION) == 0) {
" error reading .SUNW_signature data\n",
return;
}
if (!(filesig_version == FILESIG_VERSION1 ||
filesig_version == FILESIG_VERSION3)) {
/* skip versions we don't understand */
return;
}
break;
}
}
}
}
static void
{
return; /* already on the list */
}
else
}
}
static int
{
char *d, *p, *q;
int c;
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) {
dep[p - d] = '\0';
return (-1);
}
*q = '\0';
#ifndef KOBJ_DEBUG
if (_moddebug & MODDEBUG_LOADMSG) {
#endif /* KOBJ_DEBUG */
"%s: unable to resolve dependency, ",
modp->mod_modname);
modname);
#ifndef KOBJ_DEBUG
}
#endif /* KOBJ_DEBUG */
if (err_modname == NULL) {
/*
* This must be the same size as the modname
* one.
*/
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.
*/
}
continue;
}
}
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.
*/
return (-1);
}
return (0);
}
static int
{
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)
*/
return (err);
/*
* increase bss_size by the maximum delta that could be
* computed by the ALIGN below
*/
if (standalone)
MINALIGN, 0);
else
/* now assign addresses to all common symbols */
return (err);
}
return (0);
}
static int
{
int bss_align;
int err;
int i;
char *name;
int assign;
/*
* Nothing left to do (optimization).
*/
return (0);
bss_align = 0;
err = 0;
/*
* we know that st_name is in bounds, since get_sections
* has already checked all of the symbols
*/
continue;
#if defined(__sparc)
/*
* Register symbols are ignored in the kernel
*/
if (*name != '\0') {
name);
err = DOSYM_UNDEF;
}
continue;
}
#endif /* __sparc */
/*
* TLS symbols are ignored in the kernel
*/
name);
err = DOSYM_UNDEF;
continue;
}
continue;
}
}
resolved = 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)
*/
/*
* Try to determine whether this symbol
* represents a dependency on obsolete
* unsafe driver support. This is just
* to make the warning more informative.
*/
err = DOSYM_UNSAFE;
if (err == 0)
err = DOSYM_UNDEF;
}
continue;
}
/*
* It's a common symbol - st_value is the
* required alignment.
*/
if (assign) {
}
}
if (err)
return (err);
} else if (resolved) {
}
return (0);
}
kobj_hash_name(const char *p)
{
uint_t g;
hval = 0;
while (*p) {
if ((g = (hval & 0xf0000000)) != 0)
hval ^= g >> 24;
hval &= ~g;
}
return (hval);
}
/* look for name in all modules */
{
if (kernelonly)
return (0); /* didn't find it in the kernel so give up */
do {
break;
}
return (value);
}
/* look for a symbol near value. */
char *
{
/*
* Loop through the primary kernel modules.
*/
return (name);
}
do {
break;
return (name);
}
/* return address of symbol and size */
{
else
return (0);
}
{
return (0);
}
char *
{
char *strtabptr;
int symnum;
return (NULL); /* not in this module */
/*
* Scan the module's symbol table for a symbol <= value
*/
continue;
continue;
}
continue;
/*
* If one or both are functions...
*/
/* Ignore if the address is out of the bounds */
continue;
/* Prefer the function to the non-function */
continue;
/* Prefer the larger of the two functions */
continue;
}
continue;
}
}
return (NULL);
}
Sym *
{
return (sp);
return (sp);
}
/*
* Loop through the primary kernel modules.
*/
continue;
return (sp);
}
return (NULL);
}
Sym *
{
/*
* Loop through the primary kernel modules.
*/
continue;
return (sp);
}
return (NULL);
}
static Sym *
{
char *name1;
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 *
{
return (ksp);
}
return (NULL);
}
static void
{
#ifdef KOBJ_DEBUG
if (kobj_debug & D_SYMBOLS) {
"krtld: symbol entry: file=%s\n",
"krtld:\tsymndx\tvalue\t\t"
"symbol name\n");
}
}
#endif
;
}
}
struct modctl *
{
do {
return (mctl);
return (NULL);
}
/*
* Determine if the module exists.
*/
int
{
#ifdef MODDIR_SUFFIX
#endif /* MODDIR_SUFFIX */
return (0);
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 *
{
char *p, *q;
char *pathp;
char *pathpsave;
char *fullname;
int maxpathlen;
#if !defined(MODDIR_SUFFIX)
#endif
if (!use_path)
else
/* use configured default path */
/*
* 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.)
*/
/* sizeof includes null */
for (;;) {
p = fullname;
*p++ = '/'; /* path must start with '/' */
*p++ = *pathp++;
*p++ = '/';
if (use_moddir_suffix) {
char *s;
/* copy everything up to the base name */
q = name;
while (q != b && *q)
*p++ = *q++;
while (*s)
*p++ = *s++;
/* copy the rest */
while (*b)
*p++ = *b++;
} else {
q = name;
while (*q)
*p++ = *q++;
}
*p = 0;
return (file);
}
pathp++;
if (*pathp == 0)
break;
}
if (_moddebug & MODDEBUG_ERRMSG) {
}
return ((struct _buf *)-1);
}
{
int fd;
if (_modrootloaded) {
int Errno;
/*
* Hand off the open to a thread who has a
* stack size capable handling the request.
*/
} else {
/*
* 1098067: module creds should not be those of the
* caller
*/
0, 0, rootdir, -1);
}
if (Errno) {
if (_moddebug & MODDEBUG_ERRMSG) {
"kobj_open: vn_open of %s fails, ",
filename);
}
return (-1);
} else {
if (_moddebug & MODDEBUG_ERRMSG) {
}
}
} else {
if (_moddebug & MODDEBUG_ERRMSG) {
if (fd < 0)
"kobj_open: can't open %s\n", filename);
else {
}
}
}
}
/*
* Calls to kobj_open() are handled off to this routine as a separate thread.
*/
static void
{
0, 0);
thread_exit();
}
/*
* allocate and initialize a kobjopen thread structure
*/
static struct kobjopen_tctl *
{
return (ltp);
}
/*
* free a kobjopen thread control structure
*/
static void
{
}
int
{
int stat;
if (_modrootloaded) {
&resid)) != 0) {
"vn_rdwr failed with error 0x%x\n", stat);
return (-1);
}
} else {
int count = 0;
"kobj_read: seek 0x%x failed\n", offset);
return (-1);
}
if (_moddebug & MODDEBUG_ERRMSG) {
"kobj_read: req %d bytes, ", size);
}
}
return (count);
}
}
void
{
if (_moddebug & MODDEBUG_ERRMSG)
if (_modrootloaded) {
} else
(void) kobj_boot_close((int)descr);
}
int
{
return (-1);
if (_modrootloaded) {
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.
*/
return (0);
}
}
struct _buf *
{
return ((struct _buf *)-1);
}
/*
* Before root is mounted, we must check
* for a compressed file and do our own
* buffering.
*/
if (_modrootloaded) {
/* Check if the file is compressed */
} else {
return ((struct _buf *)-1);
}
return ((struct _buf *)-1);
}
} else {
}
}
return (file);
}
static int
{
/*
* read the compressed image into memory,
* so we can deompress from there
*/
return (-1);
}
return (-1);
}
return (0);
}
void
{
}
int
{
int count = 0;
int page_addr;
if (_moddebug & MODDEBUG_ERRMSG) {
}
/*
* Handle compressed (gzip for now) file here. First get the
* compressed size, then read the image into memory and finally
* call zlib to decompress the image at the supplied memory buffer.
*/
int err = 0;
return (-1);
/* Read the compressed file into memory */
&resid)) != 0) {
"error code 0x%x\n", err);
return (-1);
}
/* Decompress the image at the supplied memory buffer */
"failed, error code : 0x%x\n", err);
return (-1);
}
"failed to uncompress (size returned 0x%x , "
return (-1);
}
return (0);
}
while (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 there's nothing to copy, we're at EOF.
*/
if (c_size <= 0)
break;
if (buf) {
if (_moddebug & MODDEBUG_ERRMSG)
c_size);
} else {
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 (c_size < 0) {
count = -1;
break;
}
break;
/*
* Otherwise, read into our buffer and copy next time
* around the loop.
*/
} else {
/*
* If a _filbuf call or nothing read, break.
*/
break;
}
}
if (_moddebug & MODDEBUG_ERRMSG)
}
}
if (_moddebug & MODDEBUG_ERRMSG)
return (count);
}
static int
{
int ret;
int nret;
ret = 0;
if (nret == -1)
return (-1);
break;
}
} else
return (ret);
}
static int
{
int i;
else
return (-1);
return (dlen);
}
int
{
return (kobj_getc(f));
return (-1);
}
void
{
if (standalone)
return;
}
void *
{
void *v;
}
return (v);
}
void *
{
/*
* 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) {
#if defined(_OBP)
#else
#endif
}
}
/*
* 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)
{
/*
*/
if (default_path != NULL)
else
/*
* Move symbol tables from boot memory to ksyms_arena.
*/
}
}
{
/*
* If we are using "large" mappings for the kernel,
* request aligned memory from boot using the
* "large" pagesize.
*/
if (lg_pagesize) {
}
#if defined(__sparc)
/* account for redzone */
if (limit)
limit -= alloc_pgsz;
#endif /* __sparc */
/*
* Need more pages?
*/
/*
* Check for overlapping segments.
*/
return ((caddr_t)0);
}
return (NULL);
}
}
}
/*
* 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.
*/
{
int f;
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
{
int err = 0;
if (_modrootloaded) {
return (EIO);
/*
* Read the last 4 bytes of the compressed (gzip)
* image to get the size of its uncompressed
* version.
*/
!= 0) {
"vn_rdwr() failed with error 0x%x\n", err);
return (-1);
}
}
} else {
#if defined(_OBP)
return (EIO);
else
#else
char *buf;
int count;
do {
if (count < 0) {
return (EIO);
}
#endif
}
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);
}
void
{
}
int
{
return (lg_pagesize);
}
void
{
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.)
* the text to be patched without calling hot_patch_kernel_text()
* (which is quite slow).
*/
uintptr_t i;
}
} else {
}
}
void
{
return;
}
static char *
{
int lmi;
}
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 *
{
/*
* check for $MACROS between nulls or slashes
*/
if (p == NULL)
return (NULL);
break;
}
valid_macro = 0;
if (lmi < NLIBMACROS) {
/*
* The following checks are used to restrict expansion of
* 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.
*/
c = *(p + macrolen + 1);
if (c == '/' || c == '\0')
valid_macro = 1;
}
}
if (!valid_macro) {
/*
* if no more macro to expand, then just copy whatever left
* and check whether it exists
*/
(struct _buf *)-1) {
return (path);
} else
return (NULL);
} else {
/*
* copy all chars before '/' and call expand_libmacro()
* again
*/
*(pathend) = '\0';
}
}
more_macro = 0;
if (c != '\0') {
more_macro = 1;
} else
/*
* copy lmi_list and split it into components.
* then put the part of tail before $MACRO into path
* at pathend
*/
if (diff > 0)
if (p2) {
} else {
}
/* copy endp only if there isn't any more macro to expand */
/*
* if more macros to expand then call expand_libmacro(),
* else return path which has the whole path
*/
return (path);
}
}
if (p2)
else
return (NULL);
}
return (NULL);
}
static void
{
(void) kobj_notify_add(entry);
}
/* ARGSUSED */
static void
{
tnf_probe_control_t **p;
tnf_tag_data_t **q;
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;
}
int
{
int result = 0;
if (plist) {
if (!boot_load)
if (!boot_load)
result = 1;
}
if (tlist) {
if (!boot_load)
if (!boot_load)
result = 1;
}
add_notify = 0;
}
return (result);
}
char *kobj_file_buf;
int kobj_file_bufsize;
/*
* This code is for the purpose of manually recording which files
* needs to go into the boot archive on any given system.
*
* and reboot the system, then use mdb to look at kobj_file_buf.
*/
static void
{
static char *buf;
static int size = 0;
int n;
if (kobj_file_bufsize == 0) /* don't bother */
return;
}
if (n > size)
n = size;
size -= n;
buf += n;
}
static int
{
#if defined(_OBP)
if (!standalone && _ioquiesced)
return (-1);
#else
#endif
}
static int
{
#if defined(_OBP)
/*
* 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);
#else /* x86 */
#endif
}
static int
{
#if defined(_OBP)
if (!standalone && _ioquiesced)
return (-1);
#else /* x86 */
#endif
}
/*ARGSUSED*/
static int
{
#if defined(_OBP)
#else
#endif
}
static int
{
#if defined(_OBP)
#else
#endif
}
static int
{
}
/*
* Check if the file is compressed (for now we handle only gzip).
* It returns CH_MAGIC_GZIP if the file is compressed and 0 otherwise.
*/
static int
{
int err = 0;
"error code 0x%x\n", err);
return (0);
}
if (magic_buf == CH_MAGIC_GZIP)
return (CH_MAGIC_GZIP);
return (0);
}