elf.c revision 481bba9e11faa7e8a2730d9ea6451f2a2272b96e
/*
* 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 (c) 1988 AT&T
* All Rights Reserved
*/
/*
* Object file dependent support for ELF objects.
*/
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <dlfcn.h>
#include <debug.h>
#include <conv.h>
#include "_rtld.h"
#include "_audit.h"
#include "_elf.h"
#include "_inline.h"
#include "msg.h"
/*
* Default and secure dependency search paths.
*/
static Spath_defn _elf_def_dirs[] = {
#if defined(_ELF64)
#else
#endif
{ 0, 0 }
};
static Spath_defn _elf_sec_dirs[] = {
#if defined(_ELF64)
#else
#endif
{ 0, 0 }
};
/*
* Defines for local functions.
*/
static Addr elf_entry_point(void);
static Alist **elf_get_def_dirs(void);
static Alist **elf_get_sec_dirs(void);
/*
* Functions and data accessed through indirect pointers.
*/
};
/*
* Default and secure dependency search paths.
*/
static Alist **
{
if (elf_def_dirs == NULL)
return (&elf_def_dirs);
}
static Alist **
{
if (elf_sec_dirs == NULL)
return (&elf_sec_dirs);
}
/*
* Redefine NEEDED name if necessary.
*/
static int
{
/*
* For ABI compliance, if we are asked for ld.so.1, then really give
*/
if (((*name == '/') &&
/* BEGIN CSTYLED */
#if defined(_ELF64)
#else
#endif
/* END CSTYLED */
return (0);
return (1);
}
}
/*
* Determine whether this object requires any hardware or software capabilities.
*/
static int
{
int cnt;
/* LINTED */
continue;
/* LINTED */
/*
* Verify the hardware capabilities.
*/
return (0);
/*
* Retain this hardware capabilities value for
* possible later inspection should this object
* be processed as a filtee.
*/
}
/*
* Verify the software capabilities.
*/
return (0);
}
cptr++;
}
}
return (1);
}
/*
* Determine if we have been given an ELF file and if so determine if the file
* is compatible. Returns 1 if true, else 0 and sets the reject descriptor
* with associated error information.
*/
Fct *
{
/*
* Determine if we're an elf file. If not simply return, we don't set
* any rejection information as this test allows use to scroll through
* the objects we support (ELF, AOUT).
*/
return (NULL);
}
/*
* Check class and encoding.
*/
/* LINTED */
return (NULL);
}
return (NULL);
}
return (NULL);
}
/*
* Verify ELF version.
*/
return (NULL);
}
/*
* Verify machine specific flags.
*/
return (NULL);
/*
* object is an explicitly defined shared object under inspection by
* ldd(1), and contains an incompatible hardware capabilities
* requirement, then inform the user, but continue processing.
*/
const char *fmt;
else
return (&elf_fct);
}
return (NULL);
}
return (&elf_fct);
}
/*
* The runtime linker employs lazy loading to provide the libraries needed for
* debugging, preloading .o's and dldump(). As these are seldom used, the
* standard startup of ld.so.1 doesn't initialize all the information necessary
* to perform plt relocation on ld.so.1's link-map. The first time lazy loading
* is called we get here to perform these initializations:
*
* o elf_needed() is called to set up the DYNINFO() indexes for each lazy
* dependency. Typically, for all other objects, this is called during
* analyze_so(), but as ld.so.1 is set-contained we skip this processing.
*
* o For intel, ld.so.1's JMPSLOT relocations need relative updates. These
* are by default skipped thus delaying all relative relocation processing
* on every invocation of ld.so.1.
*/
int
{
return (1);
/*
* As we need to refer to the DYNINFO() information, insure that it has
* been initialized.
*/
return (0);
#if defined(__i386)
/*
* This is a kludge to give ld.so.1 a performance benefit on i386.
* It's based around two factors.
*
* o JMPSLOT relocations (PLT's) actually need a relative relocation
* applied to the GOT entry so that they can find PLT0.
*
* o ld.so.1 does not exercise *any* PLT's before it has made a call
* to elf_lazy_load(). This is because all dynamic dependencies
* are recorded as lazy dependencies.
*/
#endif
return (1);
}
/*
* Lazy load an object.
*/
Rt_map *
int *in_nfavl)
{
const char *name;
/*
* If this dependency has already been processed, we're done.
*/
return (nlmp);
/*
* If we're running under ldd(1), indicate that this dependency has been
* processed (see test above). It doesn't matter whether the object is
* successfully loaded or not, this flag simply ensures that we don't
* repeatedly attempt to load an object that has already failed to load.
* To do so would create multiple failure diagnostics for the same
* object under ldd(1).
*/
/*
* Determine the initial dependency name.
*/
/*
* If this object needs to establish its own group, make sure a handle
* is created.
*/
/*
* Lazy dependencies are identified as DT_NEEDED entries with a
* DF_P1_LAZYLOAD flag in the previous DT_POSFLAG_1 element. The
* dynamic information element that corresponds to the DT_POSFLAG_1
* entry is free, and thus used to store the present entrance
* identifier. This identifier is used to prevent multiple attempts to
* load a failed lazy loadable dependency within the same runtime linker
* operation. However, future attempts to reload this dependency are
* still possible.
*/
/*
* Expand the requested name if necessary.
*/
return (NULL);
/*
* Provided the object on the head of the link-map has completed its
* relocation, create a new link-map control list for this request.
*/
AL_CNT_LMLISTS)) == NULL) {
return (NULL);
}
} else
/*
* Load the associated object.
*/
/*
* Remove any expanded pathname infrastructure. Reduce the pending lazy
* dependency count of the caller, together with the link-map lists
* count of objects that still have lazy dependencies pending.
*/
/*
* Finish processing the objects associated with this request, and
* create an association between the caller and this dependency.
*/
/*
* If this lazyload has failed, and we've created a new link-map
* control list to which this request has added objects, then remove
* all the objects that have been associated to this request.
*/
/*
* Finally, remove any link-map control list that was created.
*/
if (lmco != ALIST_OFF_DATA)
/*
* If this lazy loading failed, record the fact, and bump the lazy
* counts.
*/
}
return (nlmp);
}
/*
* Return the entry point of the ELF executable.
*/
static Addr
elf_entry_point(void)
{
return (addr);
}
/*
* Determine if a dependency requires a particular version and if so verify
* that the version exists in the dependency.
*/
int
{
/*
* Traverse the callers version needed information and determine if any
* specific versions are required from the dependency.
*/
/*
* Determine if a needed entry matches this dependency.
*/
continue;
/*
* Validate that each version required actually exists in the
* dependency.
*/
char *version, *define;
int found = 0;
/*
* Skip validation of versions that are marked
* INFO. This optimization is used for versions
* that are inherited by another version. Verification
* of the inheriting version is sufficient.
*
* Such versions are recorded in the object for the
* benefit of VERSYM entries that refer to them. This
* provides a purely diagnositic benefit.
*/
continue;
continue;
continue;
found++;
break;
}
/*
* If we're being traced print out any matched version
* when the verbose (-v) option is in effect. Always
* print any unmatched versions.
*/
/* BEGIN CSTYLED */
if (found) {
continue;
} else {
if (rtld_flags & RT_FL_SILENCERR)
continue;
}
/* END CSTYLED */
continue;
}
/*
* If the version hasn't been found then this is a
* candidate for a fatal error condition. Weak
* version definition requirements are silently
* ignored. Also, if the image inspected for a version
* definition has no versioning recorded at all then
* silently ignore this (this provides better backward
* compatibility to old images created prior to
* versioning being available). Both of these skipped
* diagnostics are available under tracing (see above).
*/
return (0);
}
}
}
return (1);
}
/*
* Search through the dynamic section for DT_NEEDED entries and perform one
* of two functions. If only the first argument is specified then load the
* defined shared object, otherwise add the link map representing the defined
* link map the the dlopen list.
*/
static int
{
/*
* Process each shared object on needed list.
*/
return (1);
char *name;
int silent = 0;
case DT_POSFLAG_1:
continue;
case DT_NEEDED:
case DT_USED:
((lmtflags & LML_TFLG_NOLAZYLD) == 0)) {
lazy = 1;
}
flags =
}
}
/*
* NOTE, libc.so.1 can't be lazy loaded. Although a
* lazy position flag won't be produced when a RTLDINFO
* .dynamic entry is found (introduced with the UPM in
* Solaris 10), it was possible to mark libc for lazy
* loading on previous releases. To reduce the overhead
* of testing for this occurrence, only carry out this
* check for the first object on the link-map list
* (there aren't many applications built without libc).
*/
lazy = 0;
/*
* Don't bring in lazy loaded objects yet unless we've
* been asked to attempt to load all available objects
* (crle(1) sets LD_FLAGS=loadavail). Even under
* RTLD_NOW we don't process this - RTLD_NOW will cause
* relocation processing which in turn might trigger
* lazy loading, but its possible that the object has a
* lazy loaded file with no bindings (i.e., it should
* never have been a dependency in the first place).
*/
if (lazy) {
if ((lmflags & LML_FLG_LOADAVAIL) == 0) {
continue;
}
/*
* Silence any error messages - see description
* under elf_lookup_filtee().
*/
if ((rtld_flags & RT_FL_SILENCERR) == 0) {
silent = 1;
}
}
break;
case DT_AUXILIARY:
continue;
case DT_SUNW_AUXILIARY:
continue;
case DT_FILTER:
continue;
case DT_SUNW_FILTER:
continue;
default:
continue;
}
/*
* If we're running under ldd(1), indicate that this dependency
* has been processed. It doesn't matter whether the object is
* successfully loaded or not, this flag simply ensures that we
* don't repeatedly attempt to load an object that has already
* failed to load. To do so would create multiple failure
* diagnostics for the same object under ldd(1).
*/
/*
* Establish the objects name, load it and establish a binding
* with the caller.
*/
/*
* Clean up any infrastructure, including the removal of the
* error suppression state, if it had been previously set in
* this routine.
*/
remove_plist(&palp, 0);
if (silent)
/*
* If the object could not be mapped, continue if error
* suppression is established or we're here with ldd(1).
*/
continue;
else {
return (0);
}
}
}
return (1);
}
/*
* A null symbol interpretor. Used if a filter has no associated filtees.
*/
/* ARGSUSED0 */
static Sym *
{
return (NULL);
}
/*
* Disable filtee use.
*/
static void
{
/*
* If this is an object filter, null out the reference name.
*/
/*
* Indicate that this filtee is no longer available.
*/
}
/*
* Indicate that this standard filtee is no longer available.
*/
if (SYMSFLTRCNT(lmp))
SYMSFLTRCNT(lmp)--;
} else {
/*
* Indicate that this auxiliary filtee is no longer available.
*/
if (SYMAFLTRCNT(lmp))
SYMAFLTRCNT(lmp)--;
}
}
/*
* Find symbol interpreter - filters.
* This function is called when the symbols from a shared object should
* be resolved from the shared objects filtees instead of from within itself.
*
* A symbol name of 0 is used to trigger filtee loading.
*/
static Sym *
int *in_nfavl)
{
int any;
/*
* Indicate that the filter has been used. If a binding already exists
* to the caller, indicate that this object is referenced. This insures
* we don't generate false unreferenced diagnostics from ldd -u/U or
* debugging. Don't create a binding regardless, as this filter may
* have been dlopen()'ed.
*/
if (tracing || DBG_ENABLED) {
break;
}
}
}
}
}
/*
* If this is the first call to process this filter, establish the
* filtee list. If a configuration file exists, determine if any
* filtee associations for this filter, and its filtee reference, are
* defined. Otherwise, process the filtee reference. Any token
* expansion is also completed at this point (i.e., $PLATFORM).
*/
if (rtld_flags2 & RT_FL2_FLTCFG)
AL_CNT_FILTEES, 0, 0) == 0) {
return (NULL);
}
}
}
/*
* Traverse the filtee list, dlopen()'ing any objects specified and
* using their group handle to lookup the symbol.
*/
any = 0;
int mode;
continue;
/*
* Establish the mode of the filtee from the filter. As filtees
* are loaded via a dlopen(), make sure that RTLD_GROUP is set
* and the filtees aren't global. It would be nice to have
* RTLD_FIRST used here also, but as filters got out long before
* RTLD_FIRST was introduced it's a little too late now.
*/
mode &= ~RTLD_GLOBAL;
/*
* Insure that any auxiliary filter can locate symbols from its
* caller.
*/
mode |= RTLD_PARENT;
/*
* Process any hardware capability directory. Establish a new
* link-map control list from which to analyze any newly added
* objects.
*/
return (NULL);
} else
/*
* Determine the hardware capability filtees. If none
* can be found, provide suitable diagnostics.
*/
(rtld_flags & RT_FL_WARNFLTR)) {
(void) printf(
dir);
}
}
/*
* Re-establish the originating path name descriptor, as
* the expansion of hardware capabilities filtees may
* have re-allocated the controlling Alist. Mark this
* original pathname descriptor as unused so that the
* descriptor isn't revisited for processing. Any real
* hardware capabilities filtees have been added as new
* pathname descriptors following this descriptor.
*/
/*
* Now that any hardware capability objects have been
* processed, remove any link-map control list.
*/
if (lmco != ALIST_OFF_DATA)
}
continue;
/*
* Process an individual filtee.
*/
int audit = 0;
/*
* Determine if the reference link map is already
* loaded. As an optimization compare the filtee with
* our interpretor. The most common filter is
*/
#if defined(_ELF64)
#else
#endif
/*
* Create an association between ld.so.1 and the
* filter. As an optimization, a handle for
* ld.so.1 itself (required for the dlopen()
* family filtering mechanism) shouldn't search
* any dependencies of ld.so.1. Omitting
* GPD_ADDEPS prevents the addition of any
* ld.so.1 dependencies to this handle.
*/
NULL)
/*
* Establish the filter handle to prevent any
* recursion.
*/
/*
* any return from the auditor, as we can't
* allow ignore filtering to ld.so.1, otherwise
* nothing is going to work.
*/
nlmp, 0);
} else {
/*
* Trace the inspection of this file, determine
* any auditor substitution, and seed the file
* descriptor with the originating name.
*/
continue;
/*
* Establish a new link-map control list from
* which to analyze any newly added objects.
*/
if ((lmc =
sizeof (Lm_cntl),
AL_CNT_LMLISTS)) == NULL)
return (NULL);
} else
/*
* Locate and load the filtee.
*/
FLG_RT_HANDLE, &rej);
/*
* Establish the filter handle to prevent any
* recursion.
*/
}
/*
* return of 0 indicates the auditor wishes to
* ignore this filtee.
*/
nlmp, 0) == 0) {
audit = 1;
}
}
/*
* Finish processing the objects associated with
* this request. Create an association between
* this object and the originating filter to
* provide sufficient information to tear down
* this filtee if necessary.
*/
in_nfavl) == 0)))
/*
* If the filtee has been successfully
* processed, then create an association
* between the filter and filtee. This
* association provides sufficient information
* to tear down the filter and filtee if
* necessary.
*/
/*
* Generate a diagnostic if the filtee couldn't
* be loaded.
*/
audit));
/*
* If this filtee loading has failed, and we've
* created a new link-map control list to which
* this request has added objects, then remove
* all the objects that have been associated to
* this request.
*/
/*
* Remove any link-map control list that was
* created.
*/
if (lmco != ALIST_OFF_DATA)
}
/*
* If the filtee couldn't be loaded, null out the
* path name descriptor entry, and continue the search.
* Otherwise, the group handle is retained for future
* symbol searches.
*/
continue;
}
}
/*
* If we're just here to trigger filtee loading skip the symbol
* lookup so we'll continue looking for additional filtees.
*/
if (name) {
any++;
/*
* Look for the symbol in the handles dependencies.
*/
continue;
/*
* If our parent is a dependency don't look at
* it (otherwise we are in a recursive loop).
* This situation can occur with auxiliary
* filters if the filtee has a dependency on the
* filter. This dependency isn't necessary as
* auxiliary filters are opened RTLD_PARENT, but
* users may still unknowingly add an explicit
* dependency to the parent.
*/
continue;
break;
}
/*
* If a symbol has been found, indicate the binding
* and return the symbol.
*/
if (sym) {
*binfo |= DBG_BINFO_FILTEE;
return (sym);
}
}
/*
* If this object is tagged to terminate filtee processing we're
* done.
*/
break;
}
/*
* If we're just here to trigger filtee loading then we're done.
*/
return (NULL);
/*
* If no filtees have been found for a filter, clean up any path name
* descriptors and disable their search completely. For auxiliary
* filters we can reselect the symbol search function so that we never
* enter this routine again for this object. For standard filters we
* use the null symbol routine.
*/
if (any == 0) {
return (NULL);
}
return (NULL);
}
/*
* Focal point for disabling error messages for auxiliary filters. As an
* auxiliary filter allows for filtee use, but provides a fallback should a
* filtee not exist (or fail to load), any errors generated as a consequence of
* trying to load the filtees are typically suppressed. Setting RT_FL_SILENCERR
* suppresses errors generated by eprint(), but insures a debug diagnostic is
* produced. ldd(1) employs printf(), and here, the selection of whether to
* print a diagnostic in regards to auxiliary filters is a little more complex.
*
* . The determination of whether to produce an ldd message, or a fatal
* error message is driven by LML_FLG_TRC_ENABLE.
* . More detailed ldd messages may also be driven off of LML_FLG_TRC_WARN,
* (ldd -d/-r), LML_FLG_TRC_VERBOSE (ldd -v), LML_FLG_TRC_SEARCH (ldd -s),
* and LML_FLG_TRC_UNREF/LML_FLG_TRC_UNUSED (ldd -U/-u).
*
* . If the calling object is lddstub, then several classes of message are
* suppressed. The user isn't trying to diagnose lddstub, this is simply
* a stub executable employed to preload a user specified library against.
*
* . If RT_FL_SILENCERR is in effect then any generic ldd() messages should
* be suppressed. All detailed ldd messages should still be produced.
*/
Sym *
int *in_nfavl)
{
int silent = 0;
/*
* Make sure this entry is still acting as a filter. We may have tried
* to process this previously, and disabled it if the filtee couldn't
* be processed. However, other entries may provide different filtees
* that are yet to be completed.
*/
return (NULL);
/*
* Indicate whether an error message is required should this filtee not
* be found, based on the type of filter.
*/
silent = 1;
}
if (silent)
return (sym);
}
/*
* Compute the elf hash value (as defined in the ELF access library).
* The form of the hash table is:
*
* |--------------|
* | # of buckets |
* |--------------|
* | # of chains |
* |--------------|
* | bucket[] |
* |--------------|
* | chain[] |
* |--------------|
*/
{
while (*name) {
uint_t g;
if ((g = (hval & 0xf0000000)) != 0)
hval ^= g >> 24;
hval &= ~g;
}
}
/*
* If flag argument has LKUP_SPEC set, we treat undefined symbols of type
* function specially in the executable - if they have a value, even though
* undefined, we use that value. This allows us to associate all references
* to a function's address to a single place in the process: the plt entry
* for that function in the executable. Calls to lookup from plt binding
* routines do NOT set LKUP_SPEC in the flag.
*/
Sym *
{
char *strtabptr, *strtabname;
/*
* If we're only here to establish a symbols index, skip the diagnostic
* used to trace a symbol search.
*/
return (NULL);
/* LINTED */
/*
* Get the first symbol on hash chain and initialize the string
* and symbol table pointers.
*/
return (NULL);
while (ndx) {
/*
* Compare the symbol found with the name required. If the
* names don't match continue with the next hash entry.
*/
continue;
return (NULL);
}
/*
* The Solaris ld does not put DT_VERSYM in the dynamic
* section, but the GNU ld does. The GNU runtime linker
* interprets the top bit of the 16-bit Versym value
* (0x8000) as the "hidden" bit. If this bit is set,
* the linker is supposed to act as if that symbol does
* not exist. The hidden bit supports their versioning
* scheme, which allows multiple incompatible functions
* with the same name to exist at different versions
* within an object. The Solaris linker does not support this
* mechanism, or the model of interface evolution that
* it allows, but we honor the hidden bit in GNU ld
* produced objects in order to interoperate with them.
*/
continue;
return (NULL);
}
/*
* If we're only here to establish a symbols index, we're done.
*/
return (sym);
/*
* If we find a match and the symbol is defined, return the
* symbol pointer and the link map in which it was found.
*/
*binfo |= DBG_BINFO_FOUND;
*binfo |= DBG_BINFO_INTERPOSE;
break;
/*
* If we find a match and the symbol is undefined, the
* symbol type is a function, and the value of the symbol
* is non zero, then this is a special case. This allows
* the resolution of a function address to the plt[] entry.
* See SPARC ABI, Dynamic Linking, Function Addresses for
* more details.
*/
*binfo |= DBG_BINFO_INTERPOSE;
return (sym);
}
/*
* Undefined symbol.
*/
return (NULL);
}
/*
* We've found a match. Determine if the defining object contains
* symbol binding information.
*/
/*
* If this definition is a singleton, and we haven't followed a default
* symbol search knowing that we're looking for a singleton (presumably
* because the symbol definition has been changed since the referring
* object was built), then reject this binding so that the caller can
* fall back to a standard symbol search.
*/
*binfo |= BINFO_REJSINGLE;
*binfo &= ~DBG_BINFO_MSK;
return (NULL);
}
/*
* If this is a direct binding request, but the symbol definition has
* disabled directly binding to it (presumably because the symbol
* definition has been changed since the referring object was built),
* reject this binding so that the caller can fall back to a standard
* symbol search.
*/
*binfo |= BINFO_REJDIRECT;
*binfo &= ~DBG_BINFO_MSK;
return (NULL);
}
/*
* If this is a binding request within an RTLD_GROUP family, and the
* symbol has disabled directly binding to it, reject this binding so
* that the caller can fall back to a standard symbol search.
*
* Effectively, an RTLD_GROUP family achieves what can now be
* established with direct bindings. However, various symbols have
* been tagged as inappropriate for direct binding to (ie. libc:malloc).
*
* A symbol marked as no-direct cannot be used within a group without
* first ensuring that the symbol has not been interposed upon outside
* of the group. A common example occurs when users implement their own
* version of malloc() in the executable. Such a malloc() interposes on
* the libc:malloc, and this interposition must be honored within the
* group as well.
*
* Following any rejection, LKUP_WORLD is established as a means of
* overriding this test as we return to a standard search.
*/
*binfo |= BINFO_REJGROUP;
*binfo &= ~DBG_BINFO_MSK;
return (NULL);
}
/*
* Determine whether this object is acting as a filter.
*/
return (sym);
/*
* Determine if this object offers per-symbol filtering, and if so,
* whether this symbol references a filtee.
*/
/*
* If this is a standard filter reference, and no standard
* filtees remain to be inspected, we're done. If this is an
* auxiliary filter reference, and no auxiliary filtees remain,
* we'll fall through in case any object filtering is available.
*/
(SYMSFLTRCNT(ilmp) == 0))
return (NULL);
SYMAFLTRCNT(ilmp))) {
/*
* This symbol has an associated filtee. Lookup the
* symbol in the filtee, and if it is found return it.
* If the symbol doesn't exist, and this is a standard
* filter, return an error, otherwise fall through to
* catch any object filtering that may be available.
*/
return (fsym);
return (NULL);
}
}
/*
* Determine if this object provides global filtering.
*/
/*
* This object has an associated filtee. Lookup the
* symbol in the filtee, and if it is found return it.
* If the symbol doesn't exist, and this is a standard
* filter, return and error, otherwise return the symbol
* within the filter itself.
*/
return (fsym);
}
if (flags1 & FL1_RT_OBJSFLTR)
return (NULL);
}
return (sym);
}
/*
* Create a new Rt_map structure for an ELF object and initialize
* all values.
*/
Rt_map *
{
int ndx;
/*
* If this is a shared object, the base address of the shared object is
* added to all address values defined within the object. Otherwise, if
* this is an executable, all object addresses are used as is.
*/
base = 0;
else
/*
* Traverse the program header table, picking off required items. This
* traversal also provides for the sizing of the PT_DYNAMIC section.
*/
case PT_DYNAMIC:
break;
case PT_TLS:
break;
case PT_SUNWCAP:
break;
case PT_SUNW_UNWIND:
break;
default:
break;
}
}
/*
* Determine the number of PT_DYNAMIC entries for the DYNINFO()
* allocation. Sadly, this is a little larger than we really need,
* as there are typically padding DT_NULL entries. However, adding
* this data to the initial link-map allocation is a win.
*/
if (dyn) {
}
/*
* Allocate space for the link-map, private elf information, and
* DYNINFO() data. Once these are allocated and initialized,
* remove_so(0, lmp) can be used to tear down the link-map allocation
* should any failures occur.
*/
return (NULL);
/*
* All fields not filled in were set to 0 by calloc.
*/
/*
* Fill in rest of the link map entries with information from the file's
* dynamic structure.
*/
if (dyn) {
/* CSTYLED */
case DT_SYMTAB:
break;
case DT_SUNW_SYMTAB:
SUNWSYMTAB(lmp) =
break;
case DT_SUNW_SYMSZ:
break;
case DT_STRTAB:
break;
case DT_SYMENT:
break;
case DT_FEATURE_1:
crle = 1;
break;
case DT_MOVESZ:
break;
case DT_MOVEENT:
break;
case DT_MOVETAB:
break;
case DT_REL:
case DT_RELA:
/*
* At this time, ld.so. can only handle one
* type of relocation per object.
*/
break;
case DT_RELSZ:
case DT_RELASZ:
break;
case DT_RELENT:
case DT_RELAENT:
break;
case DT_RELCOUNT:
case DT_RELACOUNT:
break;
case DT_HASH:
break;
case DT_PLTGOT:
break;
case DT_PLTRELSZ:
break;
case DT_JMPREL:
break;
case DT_INIT:
base);
break;
case DT_FINI:
base);
break;
case DT_INIT_ARRAY:
base);
break;
case DT_INIT_ARRAYSZ:
break;
case DT_FINI_ARRAY:
base);
break;
case DT_FINI_ARRAYSZ:
break;
case DT_PREINIT_ARRAY:
base);
break;
case DT_PREINIT_ARRAYSZ:
break;
case DT_RPATH:
case DT_RUNPATH:
break;
case DT_FILTER:
break;
case DT_AUXILIARY:
if (!(rtld_flags & RT_FL_NOAUXFLTR)) {
}
break;
case DT_SUNW_FILTER:
SYMSFLTRCNT(lmp)++;
break;
case DT_SUNW_AUXILIARY:
if (!(rtld_flags & RT_FL_NOAUXFLTR)) {
SYMAFLTRCNT(lmp)++;
}
break;
case DT_DEPAUDIT:
if (!(rtld_flags & RT_FL_NOAUDIT))
break;
case DT_CONFIG:
break;
case DT_DEBUG:
/*
* DT_DEBUG entries are only created in
* dynamic objects that require an interpretor
* (ie. all dynamic executables and some shared
* objects), and provide for a hand-shake with
* debuggers. This entry is initialized to
* zero by the link-editor. If a debugger has
* us and updated this entry set the debugger
* flag, and finish initializing the debugging
* structure (see setup() also). Switch off any
* configuration object use as most debuggers
* can't handle fixed dynamic executables as
* dependencies, and we can't handle requests
* like object padding for alternative objects.
*/
rtld_flags |=
break;
case DT_VERNEED:
base);
break;
case DT_VERNEEDNUM:
/* LINTED */
break;
case DT_VERDEF:
base);
break;
case DT_VERDEFNUM:
/* LINTED */
break;
case DT_VERSYM:
/*
* The Solaris ld does not produce DT_VERSYM,
* but the GNU ld does, in order to support
* their style of versioning, which differs
* from ours in some ways, while using the
* same data structures. The presence of
* DT_VERSYM therefore means that GNU
* versioning rules apply to the given file.
* If DT_VERSYM is not present, then Solaris
* versioning rules apply.
*/
base);
break;
case DT_BIND_NOW:
((rtld_flags2 & RT_FL2_BINDLAZY) == 0)) {
}
break;
case DT_FLAGS:
((rtld_flags2 & RT_FL2_BINDLAZY) == 0)) {
}
/*
* Capture any static TLS use, and enforce that
* this object be non-deletable.
*/
}
break;
case DT_FLAGS_1:
((rtld_flags2 & RT_FL2_BINDLAZY) == 0)) {
}
crle = 1;
/*
* Global auditing is only meaningful when
* specified by the initiating object of the
* process - typically the dynamic executable.
* If this is the initiaiting object, its link-
* map will not yet have been added to the
* link-map list, and consequently the link-map
* list is empty. (see setup()).
*/
else
}
/*
* If this object identifies itself as an
* interposer, but relocation processing has
* already started, then demote it. It's too
* late to guarantee complete interposition.
*/
/* BEGIN CSTYLED */
(DF_1_INTERPOSE | DF_1_SYMINTPOSE)) {
(void) printf(
else
}
/* END CSTYLED */
break;
case DT_SYMINFO:
base);
break;
case DT_SYMINENT:
break;
case DT_PLTPAD:
break;
case DT_PLTPADSZ:
break;
case DT_SUNW_RTLDINF:
/*
* Maintain a list of RTLDINFO structures.
* Typically, libc is the only supplier, and
* only one structure is provided. However,
* multiple suppliers and multiple structures
* are supported. For example, one structure
* may provide thread_init, and another
* structure may provide atexit reservations.
*/
sizeof (Rti_desc),
AL_CNT_RTLDINFO)) == NULL) {
return (NULL);
}
base);
break;
case DT_SUNW_SORTENT:
break;
case DT_SUNW_SYMSORT:
SUNWSYMSORT(lmp) =
break;
case DT_SUNW_SYMSORTSZ:
break;
case M_DT_REGISTER:
break;
}
}
else
pltpadsz);
}
}
/*
* A dynsym contains only global functions. We want to have
* a version of it that also includes local functions, so that
* dladdr() will be able to report names for local functions
* when used to generate a stack trace for a stripped file.
* This version of the dynsym is provided via DT_SUNW_SYMTAB.
*
* In producing DT_SUNW_SYMTAB, ld uses a non-obvious trick
* in order to avoid having to have two copies of the global
* symbols held in DT_SYMTAB: The local symbols are placed in
* a separate section than the globals in the dynsym, but the
* linker conspires to put the data for these two sections adjacent
* to each other. DT_SUNW_SYMTAB points at the top of the local
* symbols, and DT_SUNW_SYMSZ is the combined length of both tables.
*
* If the two sections are not adjacent, then something went wrong
* at link time. We use ASSERT to kill the process if this is
* a debug build. In a production build, we will silently ignore
* the presence of the .ldynsym and proceed. We can detect this
* situation by checking to see that DT_SYMTAB lies in
* the range given by DT_SUNW_SYMTAB/DT_SUNW_SYMSZ.
*/
ASSERT(0);
}
/*
* If configuration file use hasn't been disabled, and a configuration
* file hasn't already been set via an environment variable, see if any
* application specific configuration file is specified. An LD_CONFIG
* setting is used first, but if this image was generated via crle(1)
* then a default configuration file is a fall-back.
*/
if (cfile)
else if (crle)
}
if (rpath)
if (fltr)
/*
* For Intel ABI compatibility. It's possible that a JMPREL can be
* specified without any other relocations (e.g. a dynamic executable
* normally only contains .plt relocations). If this is the case then
* no REL, RELSZ or RELENT will have been created. For us to be able
* to traverse the .plt relocations under LD_BIND_NOW we need to know
* the RELENT for these relocations. Refer to elf_reloc() for more
* details.
*/
/*
* Establish any per-object auditing. If we're establishing `main's
* link-map its too early to go searching for audit objects so just
* hold the object name for later (see setup()).
*/
if (audit) {
if (*cp) {
return (NULL);
}
in_nfavl) == 0) {
return (NULL);
}
}
}
}
return (NULL);
}
if (cap)
/*
* Add the mapped object to the end of the link map list.
*/
/*
* Start the system loading in the ELF information we'll be processing.
*/
}
return (lmp);
}
/*
*/
void
{
case CA_SUNW_HW_1:
break;
case CA_SUNW_SF_1:
}
cap++;
}
}
/*
* Build full pathname of shared object from given directory name and filename.
*/
static char *
{
return (pname);
}
/*
* The copy relocation is recorded in a copy structure which will be applied
* after all other relocations are carried out. This provides for copying data
* that must be relocated itself (ie. pointers in shared objects). This
* structure also provides a means of binding RTLD_GROUP dependencies to any
* copy relocations that have been taken from any group members.
*
* If the size of the .bss area available for the copy information is not the
* same as the source of the data inform the user if we're under ldd(1) control
* (this checking was only established in 5.3, so by only issuing an error via
* ldd(1) we maintain the standard set by previous releases).
*/
int
{
else
AL_CNT_COPYREL) == NULL) {
return (0);
else
return (1);
}
AL_CNT_COPYREL) == NULL) {
return (0);
else
return (1);
}
}
/*
* If we are tracing (ldd), warn the user if
* 1) the size from the reference symbol differs from the
* copy definition. We can only copy as much data as the
* reference (dynamic executables) entry allows.
* 2) the copy definition has STV_PROTECTED visibility.
*/
else
}
}
}
return (1);
}
/*
* Determine the symbol location of an address within a link-map. Look for
* the nearest symbol (whose value is less than or equal to the required
* address). This is the object specific part of dladdr().
*/
static void
{
const char *str;
int _flags;
/*
* If SUNWSYMTAB() is non-NULL, then it sees a special version of
* the dynsym that starts with any local function symbols that exist in
* the library and then moves to the data held in SYMTAB(). In this
* case, SUNWSYMSZ tells us how long the symbol table is. The
* availability of local function symbols will enhance the results
* we can provide.
*
* If SUNWSYMTAB() is non-NULL, then there might also be a
* SUNWSYMSORT() vector associated with it. SUNWSYMSORT() contains
* an array of indices into SUNWSYMTAB, sorted by increasing
* address. We can use this to do an O(log N) search instead of a
* brute force search.
*
* If SUNWSYMTAB() is NULL, then SYMTAB() references a dynsym that
* contains only global symbols. In that case, the length of
* the symbol table comes from the nchain field of the related
* symbol lookup hash table.
*/
/*
* If we don't have a .hash table there are no symbols
* to look at.
*/
return;
} else {
if (dynaddr_ndx != NULL)
}
base = 0;
else
if (dynaddr_n > 0) { /* Binary search */
long mid;
/*
* Note that SUNWSYMSORT only contains symbols types that
* supply memory addresses, so there's no need to check and
* filter out any other types.
*/
addr)
addr)
} else {
break;
}
}
/*
* If the above didn't find it exactly, then we must
* return the closest symbol with a value that doesn't
* exceed the one we are looking for. If that symbol exists,
* it will lie in the range bounded by low_bnd and
* high_bnd. This is a linear search, but a short one.
*/
} else {
break;
}
}
}
} else { /* Linear search */
/*
* Skip expected symbol types that are not functions
* or data:
* - A symbol table starts with an undefined symbol
* in slot 0. If we are using SUNWSYMTAB(),
* there will be a second undefined symbol
* right before the globals.
* - The local part of SUNWSYMTAB() contains a
* series of function symbols. Each section
* starts with an initial STT_FILE symbol.
*/
continue;
continue;
continue;
/*
* Note, because we accept local and global symbols
* we could find a section symbol that matches the
* associated address, which means that the symbol
* name will be null. In this case continue the
* search in case we can find a global symbol of
* the same value.
*/
break;
}
}
if (_sym) {
if (_flags == RTLD_DL_SYMENT)
else if (_flags == RTLD_DL_LINKMAP)
} else {
/*
* addr lies between the beginning of the mapped segment and
* the first global symbol. We have no symbol to return
* and the caller requires one. We use _START_, the base
* address of the mapping.
*/
if (_flags == RTLD_DL_SYMENT) {
/*
* An actual symbol struct is needed, so we
* construct one for _START_. To do this in a
* fully accurate way requires a different symbol
* for each mapped segment. This requires the
* use of dynamic memory and a mutex. That's too much
* plumbing for a fringe case of limited importance.
*
* Fortunately, we can simplify:
* - Only the st_size and st_info fields are useful
* outside of the linker internals. The others
* reference things that outside code cannot see,
* and can be set to 0.
* - It's just a label and there is no size
* to report. So, the size should be 0.
* This means that only st_info needs a non-zero
* (constant) value. A static struct will suffice.
* It must be const (readonly) so the caller can't
* change its meaning for subsequent callers.
*/
}
}
}
static void
{
/*
* Cleanup any link-maps added to this dynamic list and free it.
*/
}
/*
* This routine is called as a last fall-back to search for a symbol from a
* standard relocation. To maintain lazy loadings goal of reducing the number
* of objects mapped, any symbol search is first carried out using the objects
* that already exist in the process (either on a link-map list or handle).
* If a symbol can't be found, and lazy dependencies are still pending, this
* routine loads the dependencies in an attempt to locate the symbol.
*
* Only new objects are inspected as we will have already inspected presently
* loaded objects before calling this routine. However, a new object may not
* be new - although the di_lmp might be zero, the object may have been mapped
* as someone elses dependency. Thus there's a possibility of some symbol
* search duplication.
*/
Sym *
{
/*
* Generate a local list of new objects to process. This list can grow
* as each object supplies its own lazy dependencies.
*/
return (NULL);
/*
* Discard any relocation index from further symbol searches.
* This index will have already been used to trigger any
* necessary lazy-loads, and it might be because one of these
* lazy loads have failed that we're here performing this
* fallback. By removing the relocation index we don't try
* and perform the same failed lazy loading activity again.
*/
sl.sl_rsymndx = 0;
/*
* Loop through the lazy DT_NEEDED entries examining each object
* for the required symbol. If the symbol is not found, the
* object is in turn added to the local alist, so that the
* objects lazy DT_NEEDED entries can be examined.
*/
continue;
/*
* If this object has already failed to lazy load, and
* we're still processing the same runtime linker
* operation that produced the failure, don't bother
* to try and load the object again.
*/
continue;
}
/*
* Try loading this lazy dependency. If the object
* can't be loaded, consider this non-fatal and continue
* the search. Lazy loaded dependencies need not exist
* and their loading should only turn out to be fatal
* if they are required to satisfy a relocation.
*
* If the file is already loaded and relocated we must
* still inspect it for symbols, even though it might
* have already been searched. This lazy load operation
* might have promoted the permissions of the object,
* and thus made the object applicable for this symbol
* search, whereas before the object might have been
* skipped.
*/
continue;
/*
* If this object isn't yet a part of the dynamic list
* then inspect it for the symbol. If the symbol isn't
* found add the object to the dynamic list so that we
* can inspect its dependencies.
*/
continue;
break;
/*
* Some dlsym() operations are already traversing a
* link-map (dlopen(0)), and thus there's no need to
* build our own dynamic dependency list.
*/
AL_CNT_LAZYFIND) == NULL) {
return (NULL);
}
}
}
if (sym)
break;
}
return (sym);
}
/*
* Warning message for bad r_offset.
*/
void
{
int trace;
(((rtld_flags & RT_FL_SILENCERR) == 0) ||
trace = 1;
else
trace = 0;
if ((trace == 0) && (DBG_ENABLED == 0))
return;
if (rsymndx) {
}
if (trace) {
const char *rstr;
return;
}
}
/*
* Resolve a static TLS relocation.
*/
long
{
/*
* Relocations against a static TLS block have limited support once
* process initialization has completed. Any error condition should be
* discovered by testing for DF_STATIC_TLS as part of loading an object,
* however individual relocations are tested in case the dynamic flag
* had not been set when this object was built.
*/
return (0);
}
/*
* If no static TLS has been set aside for this object, determine if
* any can be obtained. Enforce that any object using static TLS is
* non-deletable.
*/
if (TLSSTATOFF(lmp) == 0) {
return (0);
}
}
/*
* Typically, a static TLS offset is maintained as a symbols value.
* For local symbols that are not apart of the dynamic symbol table,
* the TLS relocation points to a section symbol, and the static TLS
* offset was deposited in the associated GOT table. Make sure the GOT
* is cleared, so that the value isn't reused in do_reloc().
*/
*(long *)roffset = 0;
} else {
}
}
}
/*
* If the symbol is not found and the reference was not to a weak symbol, report
* an error. Weak references may be unresolved.
*/
int
{
/*
* Under crle(1), relocation failures are ignored.
*/
return (1);
/*
* Under ldd(1), unresolved references are reported. However, if the
* original reference is EXTERN or PARENT these references are ignored
* unless ldd's -p option is in effect.
*/
if (((binfo & DBG_BINFO_REF_MSK) == 0) ||
}
return (1);
}
/*
* Otherwise, the unresolved references is fatal.
*/
return (0);
}
/*
* Generic relative relocation function.
*/
inline static ulong_t
{
/*
* If this relocation is against an address that is not associated with
* a mapped segment, fall back to the generic relocation loop to
* collect the associated error.
*/
return (0);
/*
* If this relocation is against a segment that does not provide write
* access, set the write permission for all non-writable mappings.
*/
return (0);
/*
* Perform the actual relocation. Note, for backward compatibility,
* SPARC relocations are added to the offset contents (there was a time
* when the offset was used to contain the addend, rather than using
* the addend itself).
*/
#if defined(__sparc)
#else
#endif
return (1);
}
/*
* When a generic relocation loop realizes that it's dealing with relative
* relocations, but no DT_RELCOUNT .dynamic tag is present, this tighter loop
* is entered as an optimization.
*/
{
char rtype;
do {
break;
break;
/*
* Make sure the next type is a relative relocation.
*/
} while (rtype == M_R_RELATIVE);
return (rbgn);
}
/*
* This is the tightest loop for RELATIVE relocations for those objects built
* with the DT_RELACOUNT .dynamic entry.
*/
{
break;
}
return (rbgn);
}