dlfcns.c revision dffec89c701f362367e5e044605265b4dede5a69
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License, Version 1.0 only 2N/A * (the "License"). You may not use this file except in compliance 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright (c) 1988 AT&T 2N/A * All Rights Reserved 2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A#
pragma ident "%Z%%M% %I% %E% SMI" 2N/A * Programmatic interface to the run_time linker. 2N/A * Determine who called us - given a pc determine in which object it resides. 2N/A * For dlopen() the link map of the caller must be passed to load_so() so that 2N/A * the appropriate search rules (4.x or 5.0) are used to locate any 2N/A * dependencies. Also, if we've been called from a 4.x module it may be 2N/A * necessary to fix the specified pathname so that it conforms with the 5.0 elf 2N/A * For dlsym() the link map of the caller is used to determine RTLD_NEXT 2N/A * requests, together with requests based off of a dlopen(0). 2N/A * For dladdr() this routines provides a generic means of scanning all loaded 2N/A * Traverse this objects mappings testing 2N/A * whether the pc falls within its range. 2N/A * No mapping can be determined. If asked for a default, assume this 2N/A * is from the executable. * External entry for dlerror(3dl). Returns a pointer to the string describing * the last occurring error. The last occurring error is cleared. * Add a dependency as a group descriptor to a group handle. Returns 0 on * failure, ALE_EXISTS if the dependency already exists, or ALE_CREATE if it * Make sure this dependency hasn't already been recorded. * Create a new handle descriptor. * Indicate this object is a part of this handles group. * Append the new dependency to this handle. * Allocate a handle and record its existence on the handle list for future * For dlopen(0) the handle is maintained as part of the link-map list, * otherwise it is associated with the referenced link-map. * Objects can contain multiple handles depending on the flags supplied. * Most RTLD flags pertain to the object itself and the bindings that it * can achieve. Multiple handles for these flags don't make sense. But * if the flag determines how the handle might be used, then multiple * handles may exist. Presently this only makes sense for RTLD_FIRST. * Determine if an appropriate handle already exists. * If this is the first dlopen() request for this handle * allocate and initialize a new handle. * Indicate that this object has been referenced. In truth a * reference hasn't yet occurred, it's a dlsym() that makes the * reference. However, we assume that anyone performing a * dlopen() will eventually call dlsym(), plus this makes for a * better diagnostic location rather than having to call * unused() after every dlsym() operation. * A dlopen(0) handle is identified by the GPH_ZERO flag, the * head of the link-map list is defined as the owner. There is * no need to maintain a list of dependencies, for when this * handle is used (for dlsym()) a dynamic search through the * entire link-map list provides for searching all objects with * As an optimization, a handle for ld.so.1 itself * (required for libdl's filtering mechanism) shouldn't * search any dependencies of ld.so.1. Omitting * GDP_ADDEPS prevents the addition of any ld.so.1 * dependencies to this handle. * If a handle already exists bump its reference count. If it's * count was 0 then this handle previously existed but could not * be removed as part of a dlclose(). Remove this handle from * the orphan list as it's once again in use. Note that handles * associated with the link-map list itself (dlopen(0)) were * never deleted or removed to the orphan list. * Once a handle is referenced, remove any stick bit. * If dlopen(..., RTLD_PARENT) add the caller to dependency list so that * it becomes part of this group. As we could be opened by different * parents this test is carried out every time a handle is requested. * Note that a parent doesn't provide symbols via dlsym() so it also * isn't necessary to add its dependencies to the handle. * Initialize a handle that has been created for an object that is already * loaded. The handle is initialized with the present dependencies of that * object. Once this initialization has occurred, any new objects that might * be loaded as dependencies (lazy-loading) are added to the handle as each new * If the handle has already been initialized, and the initial object's * mode hasn't been promoted, there's no need to recompute the modes of * any dependencies. If the object we've added has just been opened, * the objects dependencies will not yet have been processed. These * dependencies will be added on later calls to load_one(). Otherwise, * this object already exists, so add all of its dependencies to the * handle were operating on. * If this dependency doesn't indicate that its dependencies * should be added to a handle, ignore it. This case identifies * a parent of a dlopen(RTLD_PARENT) request. * Something failed. Remove the new handle. * Sanity check a program-provided handle. * If we're already at atexit() there's no point processing further, * all objects have already been tsorted for fini processing. * Diagnose what we're up to. * Determine if we've an owner for this handle. * Decrement reference count of this object. * If this handle is special (dlopen(0)), then leave it around - it * This handle is no longer being referenced, remove it. * Internal dlclose activity. Called from user level or directly for internal * Although we're deleting object(s) it's quite possible that additional * objects get loaded from running the .fini section(s) of the objects * being deleted. These objects will have been added to the same * link-map list as those objects being deleted. Remember this list * for later investigation. * Determine whether the original link-map list still exists. In the * case of a dlclose of an alternative (dlmopen) link-map the whole * list may have been removed. * Argument checking for dlclose. Only called via external entry. * External entry for dlclose(3dl). Returns 0 for success, non-zero otherwise. * Check for magic link-map list values: * LM_ID_BASE: Operate on the PRIMARY (executables) link map * LM_ID_LDSO: Operation on ld.so.1's link map * LM_ID_NEWLM: Create a new link-map. * Establish the new link-map flags from the callers and those * Unset any auditing flags - an auditor shouldn't be * audited. Insure all audit dependencies are loaded. * If the path specified is null then we're operating on global * objects. Associate a dummy handle with the link-map list. * Traverse the main link-map control list, updating the mode * of any objects as necessary. Call the relocation engine if * this mode promotes the existing state of any relocations. * crle()'s first pass loads all objects necessary for building * a configuration file, however none of them are relocated. * crle()'s second pass relocates objects in preparation for * dldump()'ing using dlopen(0, RTLD_NOW). * Fix the pathname. If this object expands to multiple paths (ie. * $ISALIST or $HWCAP have been used), then make sure the user has also * furnished the RTLD_FIRST flag. As yet, we don't support opening * more than one object at a time, so enforcing the RTLD_FIRST flag * provides flexibility should we be able to support dlopening more * than one object in the future. * Create a new link-map control list for this request, and load the * Remove any expanded pathname infrastructure, and if the dependency * couldn't be loaded, cleanup. * If loading an auditor was requested, and the auditor already existed, * then the link-map returned will be to the original auditor. The new * link-map list that was initially created, and the associated link-map * control list are no longer needed. As the auditor is already loaded, * we're probably done, but fall through in case additional relocations * would be triggered by the mode of the caller. * Finish processing the objects associated with this request. * After a successful load, any objects collected on the new link-map * control list will have been moved to the callers link-map control * list. This control list can now be deleted. * Internal dlopen() activity. Called from user level or directly for internal * opens that require a handle. * Determine the link-map that has just been loaded. * Establish the new link-map from which .init processing will * begin. Ignore .init firing when constructing a configuration * Return the number of objects loaded if required. This is used to * trigger used() processing on return from a dlopen(). * Argument checking for dlopen. Only called via external entry. * Verify that a valid pathname has been supplied. * Historically we've always verified the mode is either RTLD_NOW or * RTLD_LAZY. RTLD_NOLOAD is valid by itself. Use of LM_ID_NEWLM * requires a specific pathname, and use of RTLD_PARENT is meaningless. * External entry for dlopen(3dl). On success, returns a pointer (handle) to * the structure containing information about the newly added object, ie. can * be used by dlsym(). On failure, returns a null pointer. * External entry for dlmopen(3dl). * Handle processing for dlsym. * Continue processing a dlsym request. Lookup the required symbol in * each link-map specified by the handle. * To leverage off of lazy loading, dlsym() requests can result in two * passes. The first descends the link-maps of any objects already in * the address space. If the symbol isn't located, and lazy * dependencies still exist, then a second pass is made to load these * dependencies if applicable. This model means that in the case where * a symbols exists in more than one object, the one located may not be * constant - this is the standard issue with lazy loading. In addition, * attempting to locate a symbol that doesn't exist will result in the * loading of all lazy dependencies on the given handle, which can * defeat some of the advantages of lazy loading (look out JVM). * If this symbol lookup is triggered from a dlopen(0) handle, * traverse the present link-map list looking for promiscuous * If this handle indicates we're only to look in the * first object check whether we're done. * If we're unable to locate the symbol and this link-map still * has pending lazy dependencies, start loading them in an * attempt to exhaust the search. Note that as we're already * traversing a dynamic linked list of link-maps there's no * need for elf_lazy_find_sym() to descend the link-maps itself. * Traverse the dlopen() handle for the presently loaded * If we're unable to locate the symbol and this link-map still * has pending lazy dependencies, start loading them in an * attempt to exhaust the search. * Core dlsym activity. Selects symbol lookup method from handle. * If the handle is RTLD_NEXT start searching in the next link * map from the callers. Determine permissions from the * present link map. Indicate to lookup_sym() that we're on an * RTLD_NEXT request so that it will use the callers link map to * start any possible lazy dependency loading. * If the handle is RTLD_SELF start searching from the caller. * If the handle is RTLD_DEFAULT or RTLD_PROBE, mimic the * symbol lookup that would be triggered by a relocation. * Determine if a specific object is registered to offer this * symbol from any Syminfo information. If a registered object * is defined, it will be loaded, and directly bound to if * necessary via LM_LOOKUP_SYM(). Otherwise a serial symbol * search is carried out where permissions are determined from * RTLD_PROBE is more optimal than RTLD_DEFAULT, as no fall back * loading of pending lazy dependencies occurs. * If the symbol is defined within the caller as an * UNDEF (DBG_BINFO_FOUND isn't set), then determine * the associated syminfo index and continue the search. * Look in the shared object specified by the handle and in all * Internal dlsym activity. Called from user level or directly for internal * While looking for symbols it's quite possible that additional objects * get loaded from lazy loading. These objects will have been added to * the same link-map list as those objects on the handle. Remember this * list for later investigation. * Cache the error message, as Java tends to fall through this * Argument checking for dlsym. Only called via external entry. * External entry for dlsym(). On success, returns the address of the specified * symbol. On error returns a null. * Set up generic information and any defaults. * Determine the nearest symbol to this address. * External entry for dladdr(3dl) and dladdr1(3dl). Returns an information * structure that reflects the symbol closest to the address specified. * Use our calling technique to determine what object is associated * with the supplied address. If a caller can't be determined, * Use our calling technique to determine what object is associated * with the supplied address. If a caller can't be determined, * Verify any arguments first. * If an input file is specified make sure its one of our dependencies. * If the object being dump'ed isn't fixed identify its mapping. * As rt_dldump() will effectively lazy load the necessary support * libraries, make sure ld.so.1 is initialized for plt relocations. * Dump the required image. * External entry for dldump(3dl). Returns 0 on success, non-zero otherwise. * get_linkmap_id() translates Lm_list * pointers to the Link_map id as used by * the rtld_db and dlmopen() interfaces. It checks to see if the Link_map is * one of the primary ones and if so returns it's special token: * If it's not one of the primary link_map id's it will instead returns a * pointer to the Lm_list structure which uniquely identifies the Link_map. * Extract information for a dlopen() handle. * Return configuration cache name and address. * Return profiled object name (used by ldprof audit library). * If a profile destination directory hasn't been specified * Obtain or establish a termination signal. * Determine whether the signal is in range. * For any other request a link-map is required. Verify the handle. * Obtain the process arguments, environment and auxv. Note, as the * environment can be modified by the user (putenv(3c)), reinitialize * the environment pointer on each request. * Return Lmid_t of the Link-Map list that the specified object is * Return a pointer to the Link-Map structure associated with the * Return search path information, or the size of the buffer required * to store the information. * Traverse search path entries for this object. * If configuration information exists, it's possible * this path has been identified as non-existent, if so * Keep track of search path count and total info size. * If we're filling in search path information, confirm * there's sufficient space. * Append the path to the information buffer. * If we're here to size the search buffer fill it in. * Return the origin of the object associated with this link-map. * Basically return the dirname(1) of the objects fullpath. * External entry for dlinfo(3dl).