dlfcns.c revision f441771b0ce9f9d6122d318ff8290cb1a2848f9d
* No mapping can be determined. If asked for a default, assume this * 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. On success, returns the group descriptor, and if alep is non-NULL * the *alep is set to ALE_EXISTS if the dependency already exists, or to * ALE_CREATE if the dependency is newly created. * 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. * rlmp - represents the reference link-map for which the handle is being * clmp - represents the caller who is requesting the handle. * hflags - provide group handle flags (GPH_*) that affect the use of the * handle, such as dlopen(0), or use or use of RTLD_FIRST. * rdflags - provide group dependency flags for the reference link-map rlmp, * such as whether the dependency can be used for dlsym(), can be * relocated against, or whether this objects dependencies should * cdflags - provide group dependency flags for the caller. * For dlopen(0) the handle is maintained as part of the link-map list, * otherwise the handle is associated with the reference link-map. * Objects can contain multiple handles depending on the handle 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 * If this is the first request for this handle, allocate and * initialize a new handle. * Associate the handle with the link-map list or the reference * link-map as appropriate. * Record the existence of this handle for future verification. * 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 * If this new handle is a private handle, there's no * need to track the caller, so we're done. * If this new handle is public, and isn't a special * handle representing ld.so.1, indicate that a local * group now exists. This state allows singleton * searches to be optimized. * If a handle already exists, bump its reference count. * If the previous reference count was 0, then this is a handle * that an earlier call to dlclose() was unable to remove. Such * handles are put on the orphan list. As this handle is back * in use, it must be removed from the orphan list. * Note, handles associated with a link-map list itself (i.e. * dlopen(0)) can have a reference count of 0. However, these * handles are never deleted, and therefore are never moved to * If we've been asked to create a private handle, there's no * need to track the caller. * Negate the reference count increment. * If a private handle already exists, promote this * handle to public by initializing both the reference * count and the handle flags. * Keep track of the parent (caller). As this object can be referenced * by different parents, this processing is carried out every time a * 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. * 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. * 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. If this handle * is part of an alternative link-map list, determine if the whole list * 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. * The addition of new link-map lists is assumed to be in small quantities. * Here, we assign a unique link-map id for diagnostic use. Simply update the * running link-map count until we max out. * Having diagnosed the originally defined modes, assign any defaults * If the path specified is null then we're operating on global * objects. Associate a dummy handle with the link-map list. * Establish any flags for the handle (Grp_hdl). * - This is a dummy, public, handle (0) that provides for a * dynamic search of all global objects within the process. * - Use of the RTLD_FIRST mode indicates that only the first * dependency on the handle (the referenced object) can be * used to satisfy dlsym() requests. * Establish the flags for the referenced dependency descriptor * - The referenced object is available for dlsym(). * - The referenced object is available to relocate against. * - The referenced object should have it's dependencies * Establish the flags for this callers dependency descriptor * - The explicit creation of a handle creates a descriptor * for the referenced object and the parent (caller). * - Use of the RTLD_PARENT flag indicates that the parent * can be relocated against. * 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. * Establish a 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. * If the dlopen has failed, clean up any objects that might have been * loaded successfully on this new link-map control list. * Finally, remove any temporary link-map control list. Note, if this * operation successfully established a new link-map list, then a base * link-map control list will have been created, which must remain. * dlopen() and dlsym() operations are the means by which a process can * test for the existence of required dependencies. If the necessary * dependencies don't exist, then associated functionality can't be used. * However, the lack of dependencies can be fixed, and the dlopen() and * dlsym() requests can be repeated. As we use a "not-found" AVL tree to * cache any failed full path loads, secondary dlopen() and dlsym() requests * will fail, even if the dependencies have been installed. * dlopen() and dlsym() retry any failures by removing the "not-found" AVL * tree. Should any dependencies be found, their names are added to the * FullPath AVL tree. This routine removes any new "not-found" AVL tree, * so that the dlopen() or dlsym() can replace the original "not-found" tree. * Internal dlopen() activity. Called from user level or directly for internal * opens that require a handle. * 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. * Open the required object on the associated link-map list. * If the object could not be found it is possible that the "not-found" * AVL tree had indicated that the file does not exist. In case the * file system has changed since this "not-found" recording was made, * retry the dlopen() with a clean "not-found" AVL tree. * If the file is found, then its full path name will have been * registered in the FullPath AVL tree. Remove any new * "not-found" AVL information, and restore the former AVL tree. * Establish the new link-map from which .init processing will begin. * Ignore .init firing when constructing a configuration file (crle(1)). * If loading an auditor was requested, and the auditor already existed, * then the link-map returned will be to the original auditor. Remove * the link-map control list that was created for this request. * If this load failed, remove any alternative link-map list. * Finish this load request. If objects were loaded, .init processing * is computed. Finally, the debuggers are informed of the link-map * 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 symbol 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. * Keep track of any global pending lazy loads. * If we're unable to locate the symbol and this link-map list * 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 searching all presently loaded * Keep track of any pending lazy loads associated * If we're unable to locate the symbol and this handle still * has pending lazy dependencies, start loading the lazy * dependencies, in an attempt to exhaust the search. * Determine whether a symbol resides in a caller. This may be a reference, * which is associated with a specific dependency. * Core dlsym activity. Selects symbol lookup method from handle. * Initialize the symbol lookup data structure. * Standard relocations are evaluated using the symbol index of the * associated relocation symbol. This index provides for loading * any lazy dependency and establishing a direct binding if necessary. * If a dlsym() operation originates from an object that contains a * symbol table entry for the same name, then we need to establish the * symbol index so that any dependency requirements can be triggered. * Therefore, the first symbol lookup that is carried out is for the * symbol name within the calling object. If this symbol exists, the * symbols index is computed, added to the Slookup data, and thus used * to seed the real symbol lookup. * If a symbol reference is known, and that reference indicates * that the symbol is a singleton, then the search for the * symbol must follow the default search path. * If this handle is RTLD_NEXT determine whether a lazy load * from the caller might provide the next object. This mimics * the lazy loading initialization normally carried out by * lookup_sym(), however here, we must do this up-front, as * lookup_sym() will be used to inspect the next object. * Clear the symbol index, so as not to confuse * lookup_sym() of the next object. * 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 mimic the standard symbol * lookup as would be triggered by a relocation. * If the handle is RTLD_PROBE, mimic the standard symbol * lookup as would be triggered by a relocation, however do * not fall back to a lazy loading rescan if the symbol can't be * found within the currently loaded objects. Note, a lazy * loaded dependency required by the caller might still get * loaded to satisfy this request, but no exhaustive lazy load * Look in the shared object specified by the handle and in all * Indicate that the defining object is now used. * 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. * If the symbol could not be found it is possible that the "not-found" * AVL tree had indicated that a required file does not exist. In case * the file system has changed since this "not-found" recording was * made, retry the dlsym() with a clean "not-found" AVL tree. * If the symbol is found, then any file that was loaded will * have had its full path name registered in the FullPath AVL * tree. Remove any new "not-found" AVL information, and * restore the former AVL tree. * 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 * on the main link-map list. Note, this has really all evolved for * crle(), which uses libcrle.so on an alternative link-map to trigger * dumping objects from the main link-map list. If we ever want to * dump objects from alternative link-maps, this model is going to * 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(3c). 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. * Set a new deferred dependency name. * If this dependency has already been established, then this dlinfo() * Assign the new dependency name. * Extract information for a dlopen() handle. * Determine whether a handle is provided. A handle isn't needed for * all operations, but it is validated here for the initial diagnostic. * Validate the request and return buffer. * 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. * Return the number of object mappings, or the mapping information for * Assign a new dependency name to a deferred dependency. * A deferred dependency can be determined by referencing a * symbol family member that is associated to the dependency, * or by looking for the dependency by its name. * Lookup the symbol in the associated object. * Use the symbols index to reference the Syminfo entry * and thus find the associated dependency. * No deferred symbol found. * Using the target objects dependency information, find * the associated deferred dependency. * If this dependency name has been changed by * a previous dlinfo(), check the original * dynamic entry string. The user might be * attempting to re-change an entry using the * original name as the reference. * No deferred dependency found. * External entry for dlinfo(3dl). * GNU defined function to iterate through the program headers for all * currently loaded dynamic objects. The caller supplies a callback function * which is called for each object. * callback - Callback function to call. The arguments to the callback * info - Address of dl_phdr_info structure * size - sizeof (struct dl_phdr_info) * data - Caller supplied value. * data - Value supplied by caller, which is passed to callback without * callback is called for each dynamic ELF object in the process address * space, halting when a non-zero value is returned, or when the last * object has been processed. The return value from the last call * to callback is returned. * The Linux implementation has added additional fields to the * dl_phdr_info structure over time. The callback function is * supposed to use the size field to determine which fields are * present, and to avoid attempts to access non-existent fields. * We have added those fields that are compatible with Solaris, and * which are used by GNU C++ (g++) runtime exception handling support. * We issue a callback for every ELF object mapped into the process * address space at the time this routine is entered. These callbacks * are arbitrary functions that can do anything, including possibly * causing new objects to be mapped into the process, or unmapped. * This complicates matters: * - Adding new objects can cause the alists to be reallocated * or for contents to move. This can happen explicitly via * dlopen(), or implicitly via lazy loading. One might consider * simply banning dlopen from a callback, but lazy loading must * be allowed, in which case there's no reason to ban dlopen(). * - Removing objects can leave us holding references to freed * memory that must not be accessed, and can cause the list * items to move in a way that would cause us to miss reporting * one, or double report others. * - We cannot allocate memory to build a separate data structure, * because the interface to dl_iterate_phdr() does not have a * way to communicate allocation errors back to the caller. * Even if we could, it would be difficult to do so efficiently. * - It is possible for dl_iterate_phdr() to be called recursively * from a callback, and there is no way for us to detect or manage * this effectively, particularly as the user might use longjmp() * to skip past us on return. Hence, we must be reentrant * (stateless), further precluding the option of building a * separate data structure. * Despite these constraints, we are able to traverse the link-map * - Once interposer (preload) objects have been processed at * startup, we know that new objects are always placed at the * end of the list. Hence, if we are reading a list when that * happens, the new object will not alter the part of the list * that we've already processed. * - The alist _TRAVERSE macros recalculate the address of the * current item from scratch on each iteration, rather than * incrementing a pointer. Hence, alist additions that occur * in mid-traverse will not cause confusion. * There is one limitation: We cannot continue operation if an object * is removed from the process from within a callback. We detect when * this happens and return immediately with a -1 return value. * As currently implemented, if a callback causes an object to be loaded, * that object may or may not be reported by the current invocation of * dl_iterate_phdr(), based on whether or not we have already processed * the link-map list that receives it. If we want to prevent this, it * can be done efficiently by associating the current value of cnt_map * with each new Rt_map entered into the system. Then this function can * use that to detect and skip new objects that enter the system in * mid-iteration. However, the Linux documentation is ambiguous on whether * this is necessary, and it does not appear to matter in practice. * We have therefore chosen not to do so at this time. /* Issue a callback for each ELF object in the process */ * On 32-bit sparc, the possibility exists that * this object is not ELF. /* Prepare the object information structure */ /* Return immediately on non-zero result */ /* Adapt to object mapping changes */ /* Stop if an object was unmapped */