analyze.c revision 8af2c5b9bdbf69a55f079d7ad9483d38fae9f023
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 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 2007 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 * If a load filter flag is in effect, and this object is a filter, trigger the 2N/A * loading of all its filtees. The load filter flag is in effect when creating 2N/A * configuration files, or when under the control of ldd(1), or the LD_LOADFLTR 2N/A * environment variable is set, or this object was built with the -zloadfltr 2N/A * flag. Otherwise, filtee loading is deferred until triggered by a relocation. 2N/A * Analyze one or more link-maps of a link map control list. This routine is 2N/A * called at startup to continue the processing of the main executable. It is 2N/A * also called each time a new set of objects are loaded, ie. from filters, 2N/A * lazy-loaded objects, or dlopen(). 2N/A * In each instance we traverse the link-map control list starting with the 2N/A * initial object. As dependencies are analyzed they are added to the link-map 2N/A * control list. Thus the list grows as we traverse it - this results in the 2N/A * breadth first ordering of all needed objects. 2N/A * If this link-map control list is being analyzed, return. The object 2N/A * that has just been added will be picked up by the existing analysis 2N/A * thread. Note, this is only really meaningful during process init- 2N/A * ialization, as objects are added to the main link-map control list. 2N/A * Following this initialization, each family of objects that are loaded 2N/A * are added to a new link-map control list. 2N/A * If this object doesn't belong to the present link-map control list 2N/A * then it must already have been analyzed, or it is in the process of 2N/A * being analyzed prior to us recursing into this analysis. In either 2N/A * case, ignore the object as it's already being taken care of. 2N/A * Indicate that analyzing is under way. 2N/A * If this link map represents a relocatable object, then we 2N/A * need to finish the link-editing of the object at this point. 2N/A * Establish any dependencies this object requires. 2N/A * If we're building a configuration file, determine if this 2N/A * object is a filter and if so load its filtees. This 2N/A * traversal is only necessary for crle(1), as typical use of 2N/A * an object will load filters as part of relocation processing. 2N/A * If an interposer has been added, it will have been inserted 2N/A * in the link-map before the link we're presently analyzing. 2N/A * Break out of this analysis loop and return to the head of 2N/A * the link-map control list to analyze the interposer. Note 2N/A * that this rescan preserves the breadth first loading of 2N/A * Copy relocation test. If the symbol definition is within .bss, then it's 2N/A * zero filled, and as the destination is within .bss, we can skip copying 2N/A * zero's to zero's. However, if the destination object has a MOVE table, it's 2N/A * .bss might contain non-zero data, in which case copy it regardless. 2N/A * Relocate an individual object. 2N/A * If we're running under ldd(1), and haven't been asked to trace any 2N/A * warnings, skip any actual relocation processing. 2N/A * Relocate the objects on a link-map control list. 2N/A * If this object has already been relocated, we're done. If 2N/A * this object is being deleted, skip it, there's probably a 2N/A * relocation error somewhere that's causing this deletion. 2N/A * Indicate that relocation processing is under way. 2N/A * Relocate the object. 2N/A * Indicate that the objects relocation is complete. 2N/A * Mark this object's init is available for harvesting. Under 2N/A * ldd(1) this marking is necessary for -i (tsort) gathering. 2N/A * Process any move data (not necessary under ldd()). 2N/A * Determine if this object is a filter, and if a load filter 2N/A * flag is in effect, trigger the loading of all its filtees. 2N/A * Perform special copy relocations. These are only meaningful for 2N/A * dynamic executables (fixed and head of their link-map list). If 2N/A * this ever has to change then the infrastructure of COPY() has to 2N/A * change as presently this element is used to capture both receiver 2N/A * and supplier of copy data. 2N/A * Only copy the bits if it's from non-zero 2N/A * If this link-map control list is being relocated, return. The object 2N/A * that has just been added will be picked up by the existing relocation 2N/A * thread. Note, this is only really meaningful during process init- 2N/A * ialization, as objects are added to the main link-map control list. 2N/A * Following this initialization, each family of objects that are loaded 2N/A * are added to a new link-map control list. 2N/A * Relocate one or more link-maps of a link map control list. If this 2N/A * object doesn't belong to the present link-map control list then it 2N/A * must already have been relocated, or it is in the process of being 2N/A * relocated prior to us recursing into this relocation. In either 2N/A * case, ignore the object as it's already being taken care of, however, 2N/A * fall through and capture any relocation promotions that might have 2N/A * been established from the reference mode of this object. 2N/A * If we're generating a configuration file using crle(1), two passes 2N/A * may be involved. Under the first pass, RTLD_CONFGEN is set. Under 2N/A * this pass, crle() loads objects into the process address space. No 2N/A * relocation is necessary at this point, we simply need to analyze the 2N/A * objects to insure any directly bound dependencies, filtees, etc. 2N/A * get loaded. Although we skip the relocation, fall through to insure 2N/A * any control lists are maintained appropriately. 2N/A * If objects are to be dldump(3c)'ed, crle(1) makes a second pass, 2N/A * using RTLD_NOW and RTLD_CONFGEN. The RTLD_NOW effectively carries 2N/A * out the relocations of all loaded objects. 2N/A * Determine whether the initial link-map control list has 2N/A * started relocation. From this point, should any interposing 2N/A * objects be added to this link-map control list, the objects 2N/A * are demoted to standard objects. Their interposition can't 2N/A * be guaranteed once relocations have been carried out. 2N/A * Relocate the link-map control list. Should this relocation 2N/A * fail, clean up this link-map list. Relocations within this 2N/A * list may have required relocation promotions on other lists, 2N/A * so before acting upon these, and possibly adding more objects 2N/A * to the present link-map control list, try and clean up any 2N/A * failed objects now. 2N/A * Determine the new, and previous link-map control lists. 2N/A * Having completed this control list of objects, they can now be bound 2N/A * to from other objects. Move this control list to the control list 2N/A * that precedes it. Although this control list may have only bound to 2N/A * controls lists much higher up the control list stack, it must only 2N/A * be moved up one control list so as to preserve the link-map order 2N/A * that may have already been traversed in search of symbols. 2N/A * Determine whether existing objects that have already been relocated, 2N/A * need any additional relocations performed. This can occur when new 2N/A * objects are loaded with RTLD_NOW, and these new objects have 2N/A * dependencies on objects that are already loaded. Note, that we peel 2N/A * any relocation promotions off of one control list at a time. This 2N/A * prevents relocations from being bound to objects that might yet fail 2N/A * to relocate themselves. 2N/A * Remove the relocation promotion list, as performing more 2N/A * relocations may result in discovering more objects that need 2N/A * If the original relocation of the link-map control 2N/A * list failed, or one of the relocation promotions of 2N/A * this loop has failed, demote any pending objects 2N/A * If a relocation fails, save the error condition. 2N/A * It's possible that all new objects on the original 2N/A * link-map control list have been relocated 2N/A * successfully, but if the user request requires 2N/A * promoting objects that have already been loaded, we 2N/A * have to indicate that this operation couldn't be 2N/A * performed. The unrelocated objects are in use on 2N/A * another control list, and may continue to be used. 2N/A * If the .plt that resulted in the error is called, 2N/A * then the process will receive a fatal error at that 2N/A * time. But, the .plt may never be called. 2N/A * Having promoted any objects, determine whether additional 2N/A * dependencies were added, and if so move them to the previous 2N/A * link-map control list. 2N/A * If relocations have been successful, indicate that relocations are 2N/A * no longer active for this control list. Otherwise, leave the 2N/A * relocation flag, as this flag is used to determine the style of 2N/A * cleanup (see remove_lmc()). 2N/A * Inherit the first rejection message for possible later diagnostics. 2N/A * Any attempt to process a file that is unsuccessful, should be accompanied 2N/A * with an error diagnostic. However, some operations like searching for a 2N/A * simple filename, involve trying numerous paths, and an error message for each 2N/A * lookup is not required. Although a multiple search can fail, it's possible 2N/A * that a file was found, but was rejected because it was the wrong type. 2N/A * To satisfy these possibilities, the first failure is recorded as a rejection 2N/A * message, and this message is used later for a more specific diagnostic. 2N/A * File searches are focused at load_one(), and from here a rejection descriptor 2N/A * is passed down to various child routines. If these child routines can 2N/A * process multiple files, then they will maintain their own rejection desc- 2N/A * riptor. This is filled in for any failures, and a diagnostic produced to 2N/A * reflect the failure. The child routines then employ rejection_inherit() to 2N/A * pass the first rejection message back to load_one(). 2N/A * Note that the name, and rejection string must be duplicated, as the name 2N/A * buffer and error string buffer (see conv_ routines) may be reused for 2N/A * additional processing or rejection messages. 2N/A * Determine the object type of a file. 2N/A * If this is a directory (which can't be mmap()'ed) generate a precise 2N/A * Map in the first page of the file. When this buffer is first used, 2N/A * the mapping is a single system page. This is typically enough to 2N/A * inspect the ehdr and phdrs of the file, and can be reused for each 2N/A * file that get loaded. If a larger mapping is required to read the 2N/A * ehdr and phdrs, a new mapping is created (see elf_map_it()). This 2N/A * new mapping is again used for each new file loaded. Some objects, 2N/A * such as filters, only take up one page, and in this case this mapping 2N/A * will suffice for the file. 2N/A * If the mapping failed, and we used MAP_ALIGN, assume we're 2N/A * on a system that doesn't support this option. Try again 2N/A * without MAP_ALIGN. 2N/A * From now on we will re-use fmap->fm_maddr as the mapping address 2N/A * so we augment the flags with MAP_FIXED and drop any MAP_ALIGN. 2N/A * Search through the object vectors to determine what kind of 2N/A * If this object is an explicitly defined shared 2N/A * object under inspection by ldd, and contains a 2N/A * incompatible hardware capabilities requirement, then 2N/A * inform the user, but continue processing. 2N/A * XXXX - ldd -v for any rej failure. 2N/A * Unknown file type. 2N/A * Helper routine for is_so_matched() that consolidates matching a path name, 2N/A * or file name component of a link-map name. 2N/A * Determine whether a search name matches one of the names associated with a 2N/A * link-map. A link-map contains several names: 2N/A * . a NAME() - typically the full pathname of an object that has been 2N/A * loaded. For example, when looking for the dependency "libc.so.1", a 2N/A * search path is applied, with the eventual NAME() being "/lib/ld.so.1". 2N/A * The name of the executable is typically a simple filename, such as 2N/A * "main", as this is the name passed to exec() to start the process. 2N/A * . a PATHNAME() - this is maintained if the resolved NAME() is different 2N/A * to NAME(), ie. the original name is a symbolic link. This is also 2N/A * the resolved full pathname for a dynamic executable. 2N/A * . a list of ALIAS() names - these are alternative names by which the 2N/A * object has been found, ie. when dependencies are loaded through a 2N/A * variety of different symbolic links. 2N/A * The name pattern matching can differ depending on whether we are looking 2N/A * for a full path name (path != 0), or a simple file name (path == 0). Full 2N/A * path names typically match NAME() or PATHNAME() entries, so these link-map 2N/A * names are inspected first when a full path name is being searched for. 2N/A * Simple file names typically match ALIAS() names, so these link-map names are 2N/A * inspected first when a simple file name is being searched for. 2N/A * For all full path name searches, the link-map names are taken as is. For 2N/A * simple file name searches, only the file name component of any link-map 2N/A * names are used for comparison. 2N/A * A pathname is typically going to match a NAME() or PATHNAME(), so 2N/A * check these first. 2N/A * Typically, dependencies are specified as simple file names 2N/A * (DT_NEEDED == libc.so.1), which are expanded to full pathnames to 2N/A * open the file. The full pathname is NAME(), and the original name 2N/A * is maintained on the ALIAS() list. 2N/A * If this is a simple filename, or a pathname has failed to match the 2N/A * NAME() and PATHNAME() check above, look through the ALIAS() list. 2N/A * If we're looking for a simple filename, _is_so_matched() 2N/A * will reduce the ALIAS name to its simple name. 2N/A * Finally, if this is a simple file name, and any ALIAS() search has 2N/A * been completed, match the simple file name of NAME() and PATHNAME(). 2N/A * Files are opened by ld.so.1 to satisfy dependencies, filtees and dlopen() 2N/A * requests. Each request investigates the file based upon the callers 2N/A * environment, and once a full path name has been established a check is made 2N/A * against the FullpathNode AVL tree and a device/inode check, to ensure the 2N/A * same file isn't mapped multiple times. See file_open(). 2N/A * However, there are one of two cases where a test for an existing file name 2N/A * needs to be carried out, such as dlopen(NOLOAD) requests, dldump() requests, 2N/A * and as a final fallback to dependency loading. These requests are handled 2N/A * by is_so_loaded(). 2N/A * A traversal through the callers link-map list is carried out, and from each 2N/A * link-map, a comparison is made against all of the various names by which the 2N/A * object has been referenced. The subroutine, is_so_matched() compares the 2N/A * link-map names against the name being searched for. Whether the search name 2N/A * is a full path name or a simple file name, governs what comparisons are made. 2N/A * A full path name, which is a fully resolved path name that starts with a "/" 2N/A * character, or a relative path name that includes a "/" character, must match 2N/A * the link-map names explicitly. A simple file name, which is any name *not* 2N/A * containing a "/" character, are matched against the file name component of 2N/A * any link-map names. 2N/A * If the name is a full path name, first determine if the path name is 2N/A * registered in the FullpathNode AVL tree. 2N/A * Determine whether the name is a simple file name, or a path name. 2N/A * Loop through the callers link-map lists. 2N/A * Tracing is enabled by the LD_TRACE_LOADED_OPTIONS environment variable which 2N/A * is normally set from ldd(1). For each link map we load, print the load name 2N/A * and the full pathname of the shared object. 2N/A * The first time through trace_so() will only have lddstub on the 2N/A * link-map list and the preloaded shared object is supplied as "path". 2N/A * As we don't want to print this shared object as a dependency, but 2N/A * instead inspect *its* dependencies, return. 2N/A * Without any rejection info, this is a supplied not-found condition. 2N/A * If rejection information exists then establish what object was 2N/A * found and the reason for its rejection. 2N/A * Was an alternative pathname defined (from a configuration 2N/A * If the load name isn't a full pathname print its associated pathname 2N/A * together with all the other information we've gathered. 2N/A * Establish a link-map mode, initializing it if it has just been loaded, or 2N/A * potentially updating it if it already exists. 2N/A * A newly loaded object hasn't had its mode set yet. Modes are used to 2N/A * load dependencies, so don't propagate any parent or no-load flags, as 2N/A * these would adversely affect this objects ability to load any of its 2N/A * dependencies that aren't already loaded. RTLD_FIRST is applicable to 2N/A * this objects handle creation only, and should not be propagated. 2N/A * Establish any new overriding modes. RTLD_LAZY and RTLD_NOW should be 2N/A * represented individually (this is historic, as these two flags were 2N/A * the only flags originally available to dlopen()). Other flags are 2N/A * accumulative, but have a hierarchy of preference. 2N/A * If this load is an RTLD_NOW request and the object has already been 2N/A * loaded non-RTLD_NOW, append this object to the relocation-now list 2N/A * of the objects associated control list. Note, if the object hasn't 2N/A * yet been relocated, setting its MODE() to RTLD_NOW will establish 2N/A * full relocation processing when it eventually gets relocated. 2N/A * For patch backward compatibility the following .init collection 2N/A * If this objects .init has been collected but has not yet been called, 2N/A * it may be necessary to reevaluate the object using tsort(). For 2N/A * example, a new dlopen() hierarchy may bind to uninitialized objects 2N/A * that are already loaded, or a dlopen(RTLD_NOW) can establish new 2N/A * bindings between already loaded objects that require the tsort() 2N/A * information be recomputed. If however, no new objects have been 2N/A * added to the process, and this object hasn't been promoted, don't 2N/A * bother reevaluating the .init. The present tsort() information is 2N/A * probably as accurate as necessary, and by not establishing a parallel 2N/A * tsort() we can help reduce the amount of recursion possible between 2N/A * Determine whether an alias name already exists, and if not create one. This 2N/A * is typically used to retain dependency names, such as "libc.so.1", which 2N/A * would have been expanded to full path names when they were loaded. The 2N/A * full path names (NAME() and possibly PATHNAME()) are maintained as Fullpath 2N/A * AVL nodes, and thus would have been matched by fpavl_loaded() during 2N/A * Determine if this filename is already on the alias list. 2N/A * This is a new alias, append it to the alias list. 2N/A * Determine whether a file is already loaded by comparing device and inode 2N/A * If this is an auditor, it will have been opened on a new link-map. 2N/A * To prevent multiple occurrences of the same auditor on multiple 2N/A * link-maps, search the head of each link-map list and see if this 2N/A * object is already loaded as an auditor. 2N/A * If the file has been found determine from the new files status 2N/A * information if this file is actually linked to one we already have 2N/A * mapped. This catches symlink names not caught by is_so_loaded(). 2N/A * Generate any error messages indicating a file could not be found. When 2N/A * preloading or auditing a secure application, it can be a little more helpful 2N/A * to indicate that a search of secure directories has failed, so adjust the 2N/A * messages accordingly. 2N/A * Under ldd(1), auxiliary filtees that can't be loaded are 2N/A * ignored, unless verbose errors are requested. 2N/A * If we're dealing with a full pathname, determine whether this 2N/A * pathname is already known. Other pathnames fall through to the 2N/A * dev/inode check, as even though the pathname may look the same as 2N/A * one previously used, the process may have changed directory. 2N/A * If this path has been constructed as part of expanding a 2N/A * HWCAP directory, ignore any subdirectories. As this is a 2N/A * silent failure, where no rejection message is created, free 2N/A * the original name to simplify the life of the caller. For 2N/A * any other reference that expands to a directory, fall through 2N/A * to construct a meaningful rejection message. 2N/A * Resolve the filename and determine whether the resolved name 2N/A * is already known. Typically, the previous fpavl_loaded() 2N/A * will have caught this, as both NAME() and PATHNAME() for a 2N/A * link-map are recorded in the FullNode AVL tree. However, 2N/A * instances exist where a file can be replaced (loop-back 2N/A * mounts, bfu, etc.), and reference is made to the original 2N/A * file through a symbolic link. By checking the pathname here, 2N/A * we don't fall through to the dev/inode check and conclude 2N/A * that a new file should be loaded. 2N/A * If this pathname hasn't been loaded, save 2N/A * the resolved pathname so that it doesn't 2N/A * have to be recomputed as part of fullpath() 2N/A * If the resolved name doesn't differ from the 2N/A * original, save it without duplication. 2N/A * Having fd_pname set indicates that no further 2N/A * resolvepath processing is necessary. 2N/A * If we've been requested to load an auditor, 2N/A * and an auditor of the same name already 2N/A * exists, then the original auditor is used. 2N/A * Otherwise, if an alternatively named file 2N/A * a new name alias, and insert any alias full 2N/A * pathname in the link-map lists AVL tree. * Record in the file descriptor the existing object * that satisfies this open request. * As the file must exist for the previous stat() to * have succeeded, record the error condition. * Trace that this open has succeeded. * If the open() failed for anything other than the file not * existing, record the error condition. * Indicate any rejection. * If this pathname was resolved and duplicated, remove the * allocated name to simplify the cleanup of the callers. * Find a full pathname (it contains a "/"). * If directory configuration exists determine if this path is known. * If the configuration file states that this path is a * directory, or the path is explicitly defined as * non-existent (ie. a unused platform specific * library), then go no further. * Attempt to open the alternative path. If * this fails, and the alternative is flagged * as optional, fall through to open the * Find a simple filename (it doesn't contain a "/"). * If we're being audited tell the audit library of the file we're about * to go search for. The audit library may offer an alternative * dependency, or indicate that this dependency should be ignored. * Protect ourselves from auditor mischief, by copying any * alternative name over the present name (the present name is * maintained in a static buffer - see elf_get_so()); * If this pnode has not yet been searched for in the * configuration file go find it. * If we found a directory search for the file. * If this object specifically does not exist, or the * object can't be found in a know-all-entries * directory, continue looking. If the object does * exist determine if an alternative object exists. * Attempt to open the alternative path. * If this fails, and the alternative is * flagged as optional, fall through to * open the original path. * Protect ourselves from building an invalid pathname. * A unique file has been opened. Create a link-map to represent it, and * process the various names by which it can be referenced. * Typically we call fct_map_so() with the full pathname of the opened * file (nname) and the name that started the search (oname), thus for * libc.so.1 (DT_NEEDED). The original name is maintained on an ALIAS * list for comparison when bringing in new dependencies. If the user * specified name as a full path (from a dlopen() for example) then * there's no need to create an ALIAS. * A new file has been opened, now map it into the process. Close the * original file so as not to accumulate file descriptors. * Save the dev/inode information for later comparisons. * Insert the names of this link-map into the FullpathNode AVL tree. * Save both the NAME() and PATHNAME() is they differ. * If this is an OBJECT file, don't insert it yet as this is only a * temporary link-map. During elf_obj_fini() the final link-map is * created, and its names will be inserted in the FullpathNode AVL * Update the objects full path information if necessary. * Note, with pathname expansion in effect, the fd_pname will * be used as PATHNAME(). This allocated string will be freed * should this object be deleted. However, without pathname * expansion, the fd_name should be freed now, as it is no * If we're processing an alternative object reset the original name * for possible $ORIGIN processing. * If we were given a pathname containing a slash then the * original name is still in oname. Otherwise the original * directory is in dir->p_name (which is all we need for * Identify this as a new object. * This function loads the named file and returns a pointer to its link map. * It is assumed that the caller has already checked that the file is not * already loaded before calling this function (refer is_so_loaded()). * Find and open the file, map it into memory, add it to the end of the list * of link maps and return a pointer to the new link map. Return 0 on error. * If the file is the run time linker then it's already loaded. * If this isn't a hardware capabilities pathname, which is already a * full, duplicated pathname, determine whether the pathname contains * a slash, and if not determine the input filename (for max path * If we are passed a 'null' link-map this means that this is the first * object to be loaded on this link-map list. In that case we set the * link-map to ld.so.1's link-map. * This link-map is referenced to determine what lookup rules to use * when searching for files. By using ld.so.1's we are defaulting to * Note: This case happens when loading the first object onto * the plt_tracing link-map. * If this path resulted from a $HWCAP specification, then the best * hardware capability object has already been establish, and is * available in the calling file descriptor. Perform some minor book- * keeping so that we can fall through into common code. * If this object is already loaded, we're done. * Obtain the avl index for this object. * If the name and resolved pathname differ, duplicate the path * name once more to provide for generic cleanup by the caller. * If this object is already loaded, we're done. * No '/' - for each directory on list, make a pathname using * that directory and filename and try to open that file. * Make sure we clear the file descriptor new name in case the * following directory search doesn't provide any directories * (odd, but this can be forced with a -znodefaultlib test). * Try and locate this file. Make sure to clean up * any rejection information should the file have * been found, but not appropriate. * If this object is already loaded, we're done. * If the file couldn't be loaded, do another comparison of * loaded files using just the basename. This catches folks * who may have loaded multiple full pathname files (possibly * from setxid applications) to satisfy dependency relationships * (i.e., a file might have a dependency on foo.so.1 which has * already been opened using its full pathname). * Duplicate the file name so that NAME() is available in core files. * Note, that hardware capability names are already duplicated, but * they get duplicated once more to insure consistent cleanup in the * event of an error condition. * Finish mapping the file and return the link-map descriptor. Note, * if this request originated from a HWCAP request, re-establish the * fdesc information. For single paged objects, such as filters, the * original mapping may have been sufficient to capture the file, thus * this mapping needs to be reset to insure it doesn't mistakenly get * unmapped as part of HWCAP cleanup. * Trace an attempt to load an object. * First generate any ldd(1) diagnostics. * If we're being audited tell the audit library of the file we're * about to go search for. * The auditor can indicate that this object should be ignored. * Protect ourselves from auditor mischief, by duplicating any * alternative name. The original name has been allocated from * expand(), so free this allocation before using the audit * Having loaded an object and created a link-map to describe it, finish * processing this stage, including verifying any versioning requirements, * updating the objects mode, creating a handle if necessary, and adding this * object to existing handles if required. * If this dependency is associated with a required version insure that * the version is present in the loaded file. * If this object has indicated that it should be isolated as a group * (DT_FLAGS_1 contains DF_1_GROUP - object was built with -B group), * or if the callers direct bindings indicate it should be isolated as * a group (DYNINFO flags contains FLG_DI_GROUP - dependency followed * -zgroupperm), establish the appropriate mode. * The intent of an object defining itself as a group is to isolate the * relocation of the group within its own members, however, unless * opened through dlopen(), in which case we assume dlsym() will be used * to located symbols in the new object, we still need to associate it * with the caller for it to be bound with. This is equivalent to a * dlopen(RTLD_GROUP) and dlsym() using the returned handle. * If the object wasn't explicitly dlopen()'ed associate it with * Establish new mode and flags. * For patch backward compatibility, the following use of update_mode() * If this is a global object, ensure the associated link-map list can * be rescanned for global, lazy dependencies. * If we've been asked to establish a handle create one for this object. * Or, if this object has already been analyzed, but this reference * requires that the mode of the object be promoted, also create a * handle to propagate the new modes to all this objects dependencies. * Establish any flags for the handle (Grp_hdl). * . Use of the RTLD_FIRST flag indicates that only the first * dependency on the handle (the new object) can be used * to satisfy dlsym() requests. * Establish the flags for this callers dependency descriptor * . The creation of a handle associated a descriptor for the * new object and descriptor for the parent (caller). * Typically, the handle is created for dlopen() or for * filtering. A handle may also be created to promote * the callers modes (RTLD_NOW) to the new object. In this * the mode propagation has occurred. * . Use of the RTLD_PARENT flag indicates that the parent * can be relocated against. * Now that a handle is being created, remove this state from * the object so that it doesn't mistakenly get inherited by * Add any dependencies that are already loaded, to the handle. * If we were asked to create a handle, we're done. * If the handle was created to promote modes from the parent * (caller) to the new object, then this relationship needs to * be removed to ensure the handle doesn't prevent the new * objects from being deleted if required. If the parent is * the only dependency on the handle, then the handle can be * completely removed. However, the handle may have already * existed, in which case only the parent descriptor can be * deleted from the handle, or at least the GPD_PROMOTE flag * removed from the descriptor. * Fall through to carry out any group processing. * If the caller isn't part of a group we're done. * Determine if our caller is already associated with a handle, if so * we need to add this object to any handles that already exist. * Traverse the list of groups our caller is a member of and add this * new link-map to those groups. * If the caller 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. * If this member already exists then its dependencies will * have already been processed. * If the object we've added has just been opened, it will not * yet have been processed for its dependencies, these will be * added on later calls to load_one(). If it doesn't have any * dependencies we're also done. * Otherwise, this object exists and has dependencies, so add * all of its dependencies to the handle were operating on. * Add any dependencies of this dependency to the * dynamic dependency list so they can be further * The central routine for loading shared objects. Insures ldd() diagnostics, * handles and any other related additions are all done in one place. * If this isn't a noload request attempt to load the file. * Note, the name of the file may be changed by an auditor. * If we've loaded a library which identifies itself as not * being dlopen()'able catch it here. Let non-dlopen()'able * objects through under RTLD_CONFGEN as they're only being * mapped to be dldump()'ed. * If it's a NOLOAD request - check to see if the object * has already been loaded. * Finish processing this loaded object. * If this object has already been analyzed, then it is in use, * so even though this operation has failed, it should not be * If this object is new, and we're being audited, tell the audit * library of the file we've just opened. Note, if the new link-map * requires local auditing of its dependencies we also register its * If this path resulted from a $HWCAP specification, then the best * hardware capability object has already been establish, and is * available in the calling file descriptor. * If this object hasn't yet been mapped, re-establish * the file descriptor structure to reflect this objects * original initial page mapping. Make sure any present * file descriptor mapping is removed before overwriting * If this path originated from a $HWCAP specification, re-establish the * fdesc information. For single paged objects, such as filters, the * original mapping may have been sufficient to capture the file, thus * this mapping needs to be reset to insure it doesn't mistakenly get * unmapped as part of HWCAP cleanup. * Load one object from a possible list of objects. Typically, for requests * such as NEEDED's, only one object is specified. However, this object could * be specified using $ISALIST or $HWCAP, in which case only the first object * that can be loaded is used (ie. the best). * A Hardware capabilities requirement can itself expand into * a number of candidates. * If this pathname originated from an expanded token, use the original * for any diagnostic output. * Determine whether a symbol is defined as an interposer. * While processing direct or group bindings, determine whether the object to * which we've bound can be interposed upon. In this context, copy relocations * are a form of interposition. * If we've bound to a copy relocation definition then we need to assign * this binding to the original copy reference. Fabricate an inter- * position diagnostic, as this is a legitimate form of interposition. * Traverse the list of known interposers to determine whether any * offer the same symbol. Note, the head of the link-map could be * identified as an interposer. If it is, make sure we only look for * symbol definitions. Otherwise, skip the head of the link-map, so * that we don't bind to any .plt references, or copy-relocations * If this object provides individual symbol * interposers, make sure that the symbol we * have found is tagged as an interposer. * Indicate this binding has occurred to an * interposer, and return the symbol. * If an object specifies direct bindings (it contains a syminfo structure * describing where each binding was established during link-editing, and the * object was built -Bdirect), then look for the symbol in the specific object. * If a direct binding resolves to the definition of a copy relocated * variable, it must be redirected to the copy (in the executable) that * will eventually be made. Typically, this redirection occurs in * lookup_sym_interpose(). But, there's an edge condition. If a * directly bound executable contains pic code, there may be a * reference to a definition that will eventually have a copy made. * However, this copy relocation may not yet have occurred, because * the relocation making this reference comes before the relocation * that will create the copy. * Under direct bindings, the syminfo indicates that a copy will be * taken (SYMINFO_FLG_COPY). This can only be set in an executable. * Thus, the caller must be the executable, so bind to the destination * of the copy within the executable. * If we need to directly bind to our parent, start looking in each * Determine the parent of this explicit dependency from its * A caller can also be defined as the parent of a dlopen() * call. Determine whether this object has any handles. The * dependencies maintained with the handle represent the * explicit dependencies of the dlopen()'ed object, and the * If we need to direct bind to anything else look in the * link map associated with this symbol reference. * If we've bound to an object, determine whether that object can be * interposed upon for this symbol. * Copy relocations should start their search after the head of the * main link-map control list. * Symbol lookup routine. Takes an ELF symbol name, and a list of link maps to * search (if the flag indicates LKUP_FIRST only the first link map of the list * is searched ie. we've been called from dlsym()). * If successful, return a pointer to the symbol table entry and a pointer to * the link map of the enclosing object. Else return a null pointer. * To improve elf performance, we first compute the elf hash value and pass * it to each find_sym() routine. The elf function will use this value to * locate the symbol, the a.out function will simply ignore it. * Search the initial link map for the required symbol (this category is * selected by dlsym(), where individual link maps are searched for a * required symbol. Therefore, we know we have permission to look at * Determine whether this lookup can be satisfied by an objects direct, * or lazy binding information. This is triggered by a relocation from * the object (hence rsymndx is set). * Find the corresponding Syminfo entry for the original * If the symbol information indicates a direct binding, * determine the link map that is required to satisfy the * binding. Note, if the dependency can not be found, but a * direct binding isn't required, we will still fall through * to perform any default symbol search. * If direct bindings have been disabled, and this isn't * a translator, skip any direct binding now that we've * insured the resolving object has been loaded. * If we need to direct bind to anything, we look in * ourselves, our parent, or in the link map we've just * loaded. Otherwise, even though we may have lazily * loaded an object we still continue to search for * symbols from the head of the link map list. * If this direct binding has been disabled * (presumably because the symbol definition has * been changed since the referring object was * built), fall back to a standard symbol * If the referencing object has the DF_SYMBOLIC flag set, look in the * referencing object for the symbol first. Failing that, fall back to * Make sure this symbol hasn't explicitly been defined * If this lookup originates from a standard relocation, then traverse * all link-map lists inspecting any object that is available to this * caller. Otherwise, traverse the link-map list associate with the * To allow transitioning into a world of lazy loading dependencies see * if this link map contains objects that have lazy dependencies still * outstanding. If so, and we haven't been able to locate a non-weak * symbol reference, start bringing in any lazy dependencies to see if * the reference can be satisfied. Use of dlsym(RTLD_PROBE) sets the * LKUP_NOFALBACK flag, and this flag disables this fall back. * If this request originated from a dlsym(RTLD_NEXT) then start * looking for dependencies from the caller, otherwise use the * If the caller is restricted to a symbol search within its group, * determine if it is necessary to follow a binding from outside of * Associate a binding descriptor with a caller and its dependency, or update * an existing descriptor. * Determine whether a binding descriptor already exists between the * Create a new binding descriptor. * Append the binding descriptor to the caller and the * Cleanup after relocation processing. * Establish bindings to all objects that have been bound to. * If we write enabled the text segment to perform these relocations * re-protect by disabling writes.