DLOPEN(3C)DLOPEN(3C) NAME dlopen, sgidlopen_version - Opens a Dynamic Shared Object (DSO) SYNOPSIS cc [options ...] file ... -lc [library ...] #include <dlfcn.h> (Required for both dlopen and for sgidlopen_version) #include <elf.h> (Required for sgidlopen_version only) void *dlopen(const char *pathname, int mode1 [ | mode2 ]); void *sgidlopen_version(const char *pathname, int mode1 [ | mode2 ], const char *version, int flags); DESCRIPTION dlopen and sgidlopen_version belong to a family of routines that provides direct access to the dynamic linking facilities. These routines are available in a library that is loaded if the -lc option is used with the CC(1), cc(1), f90(1), f77(1), or ld(1) command lines. dlopen makes a shared object available to a running process. It returns a handle to the process, which the process can use on subsequent calls to dlsym(3C) and dlclose(3C). This handle should not be interpreted in any way by the process. sgidlopen_version dynamically loads DSOs in a way similar to dlopen. However, unlike dlopen, the interface version of the DSO is specified (on the version and flags arguments) so that the dynamic linker tries to map in the DSO with the matching interface version. Aside from the restriction to a particular DSO version, the effect of sgidlopen_version is identical to that of dlopen. These routines accept the following arguments: pathname Specifies the path name of the object to be opened. It can be an absolute path or it can be relative to the current directory. If the value of pathname is 0, dlopen makes the symbols contained in the original a.out file, and all of the objects that were loaded at program startup with the a.out file, available through dlsym(3C). mode1 Specifies when relocations can occur. This is the primary mode. When a DSO is brought into the address space of a process, it might contain references to symbols whose addresses are not known until the object is loaded. These references must be relocated before the symbols can be accessed. mode1 governs when these relocations occur. Specify either RTLD_LAZY or RTLD_NOW for mode1, as follows: mode1 Argument RTLD_LAZY Specifies that only references to data symbols are relocated when the object is loaded. References to functions are not relocated until a given function is invoked for the first time. This mode should result in the best performance because a process cannot reference all of the functions in any given shared object. RTLD_NOW Specifies that all necessary relocations are performed when the object is first loaded. This might result in some wasted effort if relocations are performed for functions that are never referenced, but it is useful for programs that need to know as soon as an object is loaded that all symbols referenced during execution are available. mode2 Refines the mode1 specification. This is the secondary mode. You must include a mode1 specification in order to specify a mode2. The mode2 argument must be preceded by an OR bar (|). Specify either RTLD_GLOBAL or RTLD_LOCAL for mode2. If mode2 is not specified, RTLD_LOCAL is assumed. The mode2 specifications are as follows: mode2 Argument RTLD_GLOBAL Modifies the treatment of the symbols in the DSO being opened to be identical to those of sgidladd(3C). With RTLD_GLOBAL, the returned handle is less necessary than without RTLD_GLOBAL because rld(5) directly resolves references to symbols when RTLD_GLOBAL is specified and dlsym(3C) is not needed. RTLD_LOCAL Specifies that symbols in the DSO are not made globally visible. Default. For more information on the effects of RTLD_GLOBAL and RTLD_LOCAL, see the "NAMESPACE ISSUES" and "SYMBOL VISIBILITY" sections of this man page. version Specify a string that identifies the software version number. For more information on version strings, see the ld(1) man page and the How are multiple versions of DSOs supported? question on the dso(5) man page. The version should be a single version, such as sgi2.0. Multiple versions are not accepted, meaning sgi2.0:sgi2.1 is incorrect as a value of the version string. While the effect of passing multiple versions in the string is undefined, the effect has been to treat sgi2.0:sgi2.1 as if it were sgi2.0, unless LL_REQUIRE_MINOR was set in the flags argument. If multiple versions are in the version argument, and LL_REQUIRE_MINOR is in the flags argument, version matching fails. flags Specify either LL_NONE or LL_REQUIRE_MINOR. Specifying LL_NONE means that the exact version must be present, but the minor version number is ignored in comparisons. Specifying LL_REQUIRE_MINOR means that only that precise major and minor version are accepted. NAMESPACE ISSUES This section does not address symbol resolution from dlsym(3C). For details on its symbol resolution rules, see the dlsym(3C) man page. Name resolution can become surprisingly complicated and nonintuitive in the presence of programs or DSOs that use dlopen, sgidladd(3C), sgidlopen_version, or LL_DELAY_LOAD (the -delay_load option to the ld(1) command). If there is only one definition of a symbol across all loaded DSOs, that definition is used if it is visible. For more information on visibility, see the "SYMBOL VISIBILITY" section of this man page. Name searches are done in the order of a single list, called the rld-list. The first rld-list entry is the program itself. Following that is the list of DSOs currently in the runtime of the program. At program startup, a breadth-first list of DSOs in the program (and, recursively their library lists) is formed in the rld-list. No DSO is added to the rld-list twice. If there is a later occurrence of a DSO, the earlier occurrence is used. For an exception, see the "NOTES" section of this man page. For any DSO library list entry marked LL_DELAY_LOAD, the referenced DSO is not loaded at program startup. An exception to the name search rule is made if a DSO is marked as DT_SYMBOLIC. In this case, all name searches from within that DSO begin at the DSO itself and continue with the standard rld-list search. For more information on this, see the -Bsymbolic option to the ld(1) command. When, as a result of a call to a function in a DSO that has been loaded by using LL_DELAY_LOAD, that DSO is loaded and the new DSO is added at the end of the rld-list. If it has any entries in its library list that are not marked LL_DELAY_LOAD, the DSOs that are not delay-loaded are added recursively (breadth-first). Depending on the order of calls to delay-loaded DSOs, the order of DSOs on the rld-list might be different from one run of a program to the next. The order of DSOs in the rld-list might not match the order in any given library list because if a DSO is already in the rld-list, it is not added a second time to the rld-list. As a result of the rule that the search order is in rld-list order, the symbol that is found can be surprising. Consider a symbol A found in DSOs B and C. Further, DSO B is before DSO C in E's library list while DSO B is after DSO C in F's library list. Neither DSO B nor DSO C are otherwise referenced. If DSO E is opened by using dlopen with a mode of RTLD_GLOBAL and it is opened before DSO F, the A from DSO B is found by dlsym(3C) from either handle. Similarly, if DSO F is opened by using dlopen with a mode of RTLD_GLOBAL and it is opened before DSO E, the A from DSO C is found by dlsym(3C) from either handle. However, if only one of the DSOs E or F is opened by using dlopen with a mode of RTLD_GLOBAL, one gets DSO B's A from DSO E's handle and one gets DSO C's A from DSO F's handle. Note that dlclose(3C) does not cause any reordering of the rld-list. When the last handle (direct or indirect) on a DSO is closed with dlclose(3C), the DSO is removed from the rld-list. Before the final dlclose(3C), the DSO remains where it was on the rld-list. SYMBOL VISIBILITY DSOs loaded by a single invocation of dlopen can import symbols from one another or from any DSO that is globally visible, but DSOs loaded by one dlopen invocation cannot directly reference symbols from DSOs loaded by a different dlopen invocation. You can reference those symbols indirectly, however, by using dlsym(3C). Globally visible DSOs are those added at program startup or via delay-load from a globally-visible object. In addition, any DSO added by sgidladd(3C), by dlopen with a mode of RTLD_GLOBAL, or by sgidlopen_version with a mode of RTLD_GLOBAL is globally visible. DSOs opened with RTLD_LOCAL are not globally visible. Even in a globally visible DSO, a symbol is invisible to any access from outside the DSO if the symbol is marked STO_HIDDEN; for an example of this see the ld(1) man page's descriptions for the -hidden_symbol or -hides_file options. From within a DSO, all symbols in that DSO are visible. From within a DSO, all globally visible symbols are visible. Consider the following set of DSOs: ld -shared -all F.a -o F.so ld -shared -all G.a -o G.so ld -shared -all E.a F.so G.so -o E.so ld -shared -all H.a F.so -o H.so Consider the following conditions: * A program contains dlopen("E.so",RTLD_LAZY) and dlopen("H.so",RTLD_LAZY). * The program uses dlsym to find functions through the two handles and calls these two functions. * Each of these calls a function that calls ff() in F.so. * ff() calls fg(), which is defined only in G.so. Logically, you could assume that the call through the function accessed via E.so would resolve to fg() in G.so and that the call through the function accessed via H.so would result in an undefined function. However, rld(5) does not attempt to determine (by walking the run-time stack or other means) the exact call stack to ff(). The call stack is not really enough; rld(5) needs to know the handle used to derive the calls. The result of the call to fg() is undefined. What happens is that fg() in G.so is called, since such would be legal if the call path were through the handle in E.so. It is unwise to depend on such behavior. SEARCHING FOR DSOs If other DSOs were link edited with pathname when pathname was built, those objects are automatically loaded by dlopen. This is subject to the LL_DELAY_LOAD library list flag. The directory search path that is used to find both pathname and the other needed objects is the same as that used by rld(5). In particular, pathname is searched for in the following locations: * The directory specified by pathname if it is not a simple file name (that is, it contains a slash (/) character). If it is not a simple file, pathname is the only location searched; the other locations are ignored. * Any path specified via the -rpath argument to ld(1) when the executable file was statically linked. * Any directory specified by the LD_LIBRARY_PATH environment variable. This environment variable should contain a colon-separated list of directories, in the same format as the PATH variable (see sh(1)). 64-bit programs examine the LD_LIBRARY64_PATH variable, and if it is not set, they examine the LD_LIBRARY_PATH variable. New 32-bit ABI programs examine the LD_LIBRARYN32_PATH variable and if it is not set, they examine the LD_LIBRARY_PATH variable. All of these variables are ignored if the process is running setuid or setgid (see exec(2)). * The default search paths. The paths differ depending on ABI. For information on the specific search paths, see the rld(5) man page. If the process is running setuid or setgid, the _RLD_ROOT, _RLDN32_ROOT, or _RLD64_ROOT variable (the exact variable name depends on the ABI being used) is ignored (see rld(5)). NOTES You can open objects whose names resolve to the same absolute or relative path name any number of times by using dlopen. However, the referenced object is loaded only once into the address space of the current process. The same object referenced by two different path names, however, can be loaded multiple times. For example, given the object /usr/home/me/mylibs/mylib.so, and assuming the current working directory is /usr/home/me/workdir, the following code results in mylibs.so being loaded twice for the current process: ... void *handle1; void *handle2; handle1 = dlopen("../mylibs/mylib.so", RTLD_LAZY); handle2 = dlopen("/usr/home/me/mylibs/mylib.so", RTLD_LAZY); ... Given the same object and current working directory, if LD_LIBRARY_PATH=/usr/home/me/mylibs, the following code results in mylibs.so being loaded only once: ... void *handle1; void *handle2; handle1 = dlopen("mylib.so", RTLD_LAZY); handle2 = dlopen("/usr/home/me/mylibs/mylib.so", RTLD_LAZY); ... Users who use dlsym(0,mode) to gain access to the symbol table of the a.out file itself should be aware that some symbols defined in the a.out file might not be available to the dynamic linker. The symbol table created by ld(1) for use by the dynamic linker might contain only a subset of the symbols defined in the a.out file, specifically those referenced by the shared objects with which the a.out file is linked. Because there is no defined mechanism for dynamic loading in nonshared programs, a program built as nonshared (for example, by using cc -non_shared) cannot usefully call dlopen, dlsym(3C), dlerror(3C), sgidlopen_version, sgidladd(3C), or dlclose(3C). The dynamic loading routines are not included in the nonshared libc.a file, so attempting to use them might result in a failure at link time. Any program built as nonshared that wishes to retain code that calls the dynamic loading routines must implement its own versions of dlopen, dlsym(3C), and so on, and simply return appropriate error values to avoid the link-time errors. Note that building programs as nonshared is not generally recommended because not all libraries are available as nonshared. Using dlclose(3C) on a DSO, with _RLD_ARGS set to -s in the environment, can cause surprising side effects. This is because dlclose(3C) forces many symbol GOT entries to be reset for re-lazy-evaluation. A result of this is that function pointers previously saved by the application or some library might hold values that could be obsolete or no longer correct. This is a problem for any dlclose(3C) process, but it is more serious when dlclose(3C) is used to close a handle on a globally-visible DSO. Symbol lookups proceed in order on a linear list, and a DSO is not opened twice with the same version number (unless different dlopen paths make the DSO name appear different to rld(5)). When multiple sgidladd(3C) processes are executed and dlclose(3C) is used to close an earlier DSO, this can change the symbol to which a call is resolved. For more information, see the "NAMESPACE ISSUES" section. EXAMPLES The following specifies the RTLD_LAZY and RTLD_LOCAL modes: dlopen("X.so",RTLD_LAZY|RTLD_LOCAL); The following example program uses dlopen and was compiled with the -32 compiler command line option: dltry.c: ------- /* Error handling code not shown. */ #include <dlfcn.h> typedef int (*xamplefuncptr)(int); int main() { void *handle; int i; xamplefuncptr fptr; handle = dlopen("greetings.so", RTLD_LAZY|RTLD_LOCAL); fptr = (xamplefuncptr)dlsym(handle, "greetings"); i = (*fptr)(3); return 0; } greetings.c: ----------- #include <stdio.h> int greetings(int num_greetings) { int i; for (i=0; i < num_greetings; i++) printf ("hello world\n"); return 1; } % cc -32 -c dltry.c greetings.c % ld -32 -shared greetings.o -soname greetings.so -o greetings.so % cc -32 dltry.o % LD_LIBRARY_PATH=. % export LD_LIBRARY_PATH % a.out hello world hello world hello world RETURN VALUES If pathname cannot be found, cannot be opened for reading, is not a DSO, or if an error occurs during the process of loading pathname or relocating its symbolic references, dlopen returns NULL. More detailed diagnostic information is available through dlerror(3C). SEE ALSO CC(1), cc(1), f77(1), f90(1), ld(1), sh(1) exec(2) dlclose(3C), dlerror(3C), dlsym(3C), sgidladd(3C), dso(5), rld(5)