aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Kerrisk <mtk.manpages@gmail.com>2019-07-03 09:45:55 +0200
committerMichael Kerrisk <mtk.manpages@gmail.com>2019-07-03 09:45:55 +0200
commit43898de48866c677269c8c1821e2ceb35bc865f6 (patch)
tree7d65ffba192ff44d988b93e0d11eacd5884e64f1
parentba20328e27c16306efa63dfcfe1b440970615062 (diff)
downloadman-pages-43898de48866c677269c8c1821e2ceb35bc865f6.tar.gz
dlopen.3: Clarify the rules for symbol resolution in a dlopen'ed object
The existing text wrongly implied that symbol look up first occurred in the object and then in main, and did not mention whether dependencies of main where used for symbol resolution. Verified by experiment: $ cat prog.c #define _GNU_SOURCE #include <link.h> #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void /* A function defined in both main and lib_x1 */ prog_x1(void) { printf("Called %s::%s\n", __FILE__, __func__); } /* The following function is forced into prog's dynamic symbol table because of the static link-time reference in lib_m1.so */ void /* A function defined in both main and lib_y1 */ prog_y1_exp(void) { printf("Called %s::%s\n", __FILE__, __func__); } /* The following function is not forced into prog's dynamic symbol table */ void /* A function defined in both main and lib_y1 */ prog_y1_noexp(void) { printf("Called %s::%s\n", __FILE__, __func__); } static int callback(struct dl_phdr_info *info, size_t size, void *data) { printf("\tName = %s\n", info->dlpi_name); return 0; } int main(int argc, char *argv[]) { void *xHandle, *yHandle; void (*funcp)(void); char *err; xHandle = dlopen("./lib_x1.so", RTLD_NOW | RTLD_GLOBAL); if (xHandle == NULL) { fprintf(stderr, "dlopen: %s\n", dlerror()); exit(EXIT_FAILURE); } yHandle = dlopen("./lib_y1.so", RTLD_NOW | RTLD_GLOBAL); if (yHandle == NULL) { fprintf(stderr, "dlopen: %s\n", dlerror()); exit(EXIT_FAILURE); } /* Optionally display the link map() */ if (argc > 1) { printf("Link map as shown from dl_iterate_phdr() callbacks:\n"); dl_iterate_phdr(callback, NULL); printf("\n"); } (void) dlerror(); /* Clear dlerror() */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" funcp = (void (*)(void)) dlsym(yHandle, "y1_enter"); #pragma GCC diagnostic pop err = dlerror(); if (err != NULL) { fprintf(stderr, "dlsym: %s", err); exit(EXIT_FAILURE); } (*funcp)(); exit(EXIT_SUCCESS); } $ cat lib_m1.c #include <stdio.h> void /* A function defined in both lib_m1 and lib_y1 */ m1_y1(void) { printf("Called %s::%s\n", __FILE__, __func__); } #if 1 void dummy(void) { extern void prog_y1_exp(void); prog_y1_exp(); /* Forces prog_y1_exp into prog's dynamic symbol table, so that it will be visible also to lib_y1.so */ } #endif $ cat lib_x1.c #include <stdio.h> void /* A function defined in both main and lib_x1 */ prog_x1(void) { printf("Called %s::%s\n", __FILE__, __func__); } void /* A function defined in both lib_x1 and lib_y1 */ x1_y1(void) { printf("Called %s::%s\n", __FILE__, __func__); } $ cat lib_y1.c #include <stdio.h> void /* A function defined in both lib_x1 and lib_y1 */ x1_y1(void) { printf("Called %s::%s\n", __FILE__, __func__); } void /* A function defined in both main and lib_y1 */ prog_y1_exp(void) { printf("Called %s::%s\n", __FILE__, __func__); } void /* A function defined in both lib_m1 and lib_y1 */ m1_y1(void) { printf("Called %s::%s\n", __FILE__, __func__); } void /* A function defined in both main and lib_y1 */ prog_y1_noexp(void) { printf("Called %s::%s\n", __FILE__, __func__); } void y1_enter(void) { extern void y2(void); printf("Called %s\n\n", __func__); prog_x1(); prog_y1_exp(); prog_y1_noexp(); x1_y1(); m1_y1(); y2(); } $ cat lib_y2.c #include <stdio.h> void y2(void) { printf("Called %s::%s\n", __FILE__, __func__); } $ cat Build.sh #!/bin/sh CFLAGS="-Wno-implicit-function-declaration -Wl,--no-as-needed" cc $CFLAGS -g -fPIC -shared -o lib_x1.so lib_x1.c cc $CFLAGS -g -fPIC -shared -o lib_y2.so lib_y2.c cc $CFLAGS -g -fPIC -shared -o lib_y1.so lib_y1.c ./lib_y2.so cc $CFLAGS -g -fPIC -shared -o lib_m1.so lib_m1.c #ED="-Wl,--export-dynamic" cc $CFLAGS $ED -Wl,--rpath,$PWD -o prog prog.c -ldl lib_m1.so $ sh Build.sh $ ./prog x Link map as shown from dl_iterate_phdr() callbacks: Name = Name = linux-vdso.so.1 Name = /lib64/libdl.so.2 Name = /home/mtk/tlpi/code/shlibs/dlopen_sym_res_expt/lib_m1.so Name = /lib64/libc.so.6 Name = /lib64/ld-linux-x86-64.so.2 Name = ./lib_x1.so Name = ./lib_y1.so Name = ./lib_y2.so Called y1_enter Called lib_x1.c::prog_x1 Called prog.c::prog_y1_exp Called lib_y1.c::prog_y1_noexp Called lib_x1.c::x1_y1 Called lib_m1.c::m1_y1 Called lib_y2.c::y2 Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>
-rw-r--r--man3/dlopen.319
1 files changed, 13 insertions, 6 deletions
diff --git a/man3/dlopen.3 b/man3/dlopen.3
index 2328831641..18801ec5da 100644
--- a/man3/dlopen.3
+++ b/man3/dlopen.3
@@ -202,14 +202,21 @@ and then all shared objects loaded by
with the flag
.BR RTLD_GLOBAL .
.PP
-External references in the shared object are resolved using the
-shared objects in that object's dependency list and any other
-objects previously opened with the
-.B RTLD_GLOBAL
-flag.
+Symbol references in the shared object are resolved using (in order):
+symbols in the link map of objects loaded for the main program and its
+dependencies;
+symbols in shared objects (and their dependencies)
+that were previously opened with
+.BR dlopen ()
+using the
+.BR RTLD_GLOBAL
+flag;
+and definitions in the shared object itself
+(and any dependencies that were loaded for that object).
+.PP
If the executable was linked with the flag "\-rdynamic"
(or, synonymously, "\-\-export\-dynamic"),
-then the global symbols in the executable will also be used
+then global symbols in the executable will also be used
to resolve references in a dynamically loaded shared object.
.PP
If the same shared object is opened again with