Handle 1: Address of global_var: 0x7fe80bf67028 Handle 2: Address of global_var: 0x7fe80bf67028 Setting global_var via handle1 to 100... global_var set to 100 Value of global_var via handle1: 100 Value of global_var via handle2: 100
如果是不同的实例则相反,会打印如下
1 2 3 4 5 6 7 8
Handle 1: Address of global_var: 0x7f876b22d028 Handle 2: Address of global_var: 0x7f876b228028 Setting global_var via handle1 to 100... global_var set to 100 Value of global_var via handle1: 100 Value of global_var via handle2: 42
#define _GNU_SOURCE #include<dlfcn.h> void *dlmopen(Lmid_t lmid, constchar *filename, int flags);
接着是对这个函数的解释
This function performs the same task as
dlopen()
—the
filename
and
flags
arguments, as well as the return value, are the same, except for the differences noted below.
The
dlmopen()
function differs from
dlopen()
primarily in that it accepts an additional argument,
lmid
, that specifies the link-map list (also referred to as a namespace) in which the shared object should be loaded. By comparison,
dlopen()
adds the dynamically loaded shared object to the same namespace as the shared object from which the
dlopen()
call is made. The
Lmid_t
type is an opaque handle that refers to a namespace.
The
lmid
argument is either the ID of an existing namespace (which can be obtained using the
dlinfo(3)
RTLD_DI_LMID
request) or one of the following special values:
LM_ID_BASE
: Load the shared object in the initial namespace (i.e., the application's namespace).
LM_ID_BASE
:在初始命名空间(即应用程序命名空间)中加载共享对象。
LM_ID_NEWLM
: Create a new namespace and load the shared object in that namespace. The object must have been correctly linked to reference all of the other shared objects that it requires, since the new namespace is initially empty.
If
filename
is
NULL
, then the only permitted value for
lmid
is
LM_ID_BASE
.
如果
filename
为
NULL
,那么
lmid
唯一允许的值是
LM_ID_BASE
。
随后指出了它的限制
NOTES
dlmopen() and namespaces
A link-map list defines an isolated namespace for the resolution of symbols by the dynamic linker. Within a namespace, dependent shared objects are implicitly loaded according to the usual rules, and symbol references are likewise resolved according to the usual rules, but such resolution is confined to the definitions provided by the objects that have been (explicitly and implicitly) loaded into the namespace.
The
dlmopen()
function permits object-load isolation—the ability to load a shared object in a new namespace without exposing the rest of the application to the symbols made available by the new object. Note that the use of the
RTLD_LOCAL
flag is not sufficient for this purpose, since it prevents a shared object's symbols from being available to any other shared object. In some cases, we may want to make the symbols provided by a dynamically loaded shared object available to (a subset of) other shared objects without exposing those symbols to the entire application. This can be achieved by using a separate namespace and the
RTLD_GLOBAL
flag.
The
dlmopen()
function also can be used to provide better isolation than the
RTLD_LOCAL
flag. In particular, shared objects loaded with
RTLD_LOCAL
may be promoted to
RTLD_GLOBAL
if they are dependencies of another shared object loaded with
RTLD_GLOBAL
. Thus,
RTLD_LOCAL
is insufficient to isolate a loaded shared object except in the (uncommon) case where one has explicit control over all shared object dependencies.
Possible uses of
dlmopen()
are plugins where the author of the plugin-loading framework can't trust the plugin authors and does not wish any undefined symbols from the plugin framework to be resolved to plugin symbols. Another use is to load the same object more than once. Without the use of
dlmopen()
, this would require the creation of distinct copies of the shared object file. Using
dlmopen()
, this can be achieved by loading the same shared object file into different namespaces.
(gdb) r Starting program: /root/cpp_test/dlmopen/main Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7fc7131 in ?? () (gdb) bt #0 0x00007ffff7fc7131 in ?? () #1 0x00005555555551ed in main ()
换成dlopen则没有问题
1 2 3 4 5 6 7 8
(gdb) r Starting program: /root/cpp_test/dlmopen/main Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7fc7131 in say_hello () from ./libhello.so (gdb) bt #0 0x00007ffff7fc7131 in say_hello () from ./libhello.so #1 0x000055555555527a in main ()
#0 0x00007f04f463991e in __reclaim_stacks () from target:/lib/x86_64-linux-gnu/libpthread.so.0 #1 0x00007f04edbeec32 in fork () from target:/lib/x86_64-linux-gnu/libc.so.6 #2 0x00007f04edb8a774 in _IO_proc_open () from target:/lib/x86_64-linux-gnu/libc.so.6 #3 0x00007f04edb8a9f8 in popen () from target:/lib/x86_64-linux-gnu/libc.so.6 #4 0x0000000000cebd5b in __python_detail::PythonImpl::load_from_file_impl (async=<optimized out>) at python/python_loader.cpp:344 #5 0x00007f04ef925399 in uv__async_io (loop=0x4cf94e0 <__python_detail::py_loop>, w=<optimized out>, events=<optimized out>) at ../deps/uv/src/unix/async.c:150 #6 0x00007f04ef938198 in uv__io_poll (loop=loop@entry=0x4cf94e0 <__python_detail::py_loop>, timeout=-1) at ../deps/uv/src/unix/linux-core.c:431 #7 0x00007f04ef925c83 in uv_run (loop=loop@entry=0x4cf94e0 <__python_detail::py_loop>, mode=mode@entry=UV_RUN_DEFAULT) at ../deps/uv/src/unix/core.c:381 #8 0x0000000000cdf38e in __python_detail::PythonImpl::py_loader_impl_thread (ptr=0x7f03f817ad20) at python/python_loader.cpp:200 #9 0x00007f04f463a6db in start_thread () from target:/lib/x86_64-linux-gnu/libpthread.so.0 #10 0x00007f04edc2ba3f in clone () from target:/lib/x86_64-linux-gnu/libc.so.6
It is a bug in the glibc version of libpthreads, which results in libraries loaded with dlmopen returning duplicates for pthread_key_create, resulting in thread-specific storage being clobbered (same key means same memory location, it's like malloc returning the same memory area multiple times).
The following patchset attempts an implementation for this: If an object is loaded with the new RTLD_SHARED flag we instead ensure that a "master" copy exists (and is flagged as no-delete) in the main namespace and a thin wrapper or clone is placed in the target namespace.
typedefstruct { Elf32_Word st_name; /* Symbol name (string tbl index) */ Elf32_Addr st_value; /* Symbol value */ Elf32_Word st_size; /* Symbol size */ unsignedchar st_info; /* Symbol type and binding */ unsignedchar st_other; /* Symbol visibility under glibc>=2.2 */ Elf32_Section st_shndx; /* Section index */ } Elf32_Sym;