Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Will it be possible one day to load and close dlls with static objects on UNIXs?

41 views
Skip to first unread message

Peter

unread,
May 27, 2011, 1:07:31 PM5/27/11
to
I was wondering about some SIGSEGV during exit().
As it turns out, using static objects inside dlls which are loaded via
dlopen and closed via dlclose is still not supported on linux.
This is 2011!
I remember having these problems in 1996 -- on various UNIXs -- on
windows this worked already then.
Guys -- this is kind of ridiculous.

Måns Rullgård

unread,
May 27, 2011, 1:11:30 PM5/27/11
to
Peter <exces...@gmail.com> writes:

___________________
/| /| | |
||__|| | Please do |
/ O O\__ NOT |
/ \ feed the |
/ \ \ trolls |
/ _ \ \ ______________|
/ |\____\ \ ||
/ | | | |\____/ ||
/ \|_|_|/ \ __||
/ / \ |____| ||
/ | | /| | --|
| | |// |____ --|
* _ | |_|_|_| | \-/
*-- _--\ _ \ // |
/ _ \\ _ // | /
* / \_ /- | - | |
* ___ c_c_c_C/ \C_c_c_c____________


--
Måns Rullgård
ma...@mansr.com

Peter

unread,
Jun 2, 2011, 7:40:39 PM6/2/11
to

This crash caused me to look deeper into the shared library system on
LINUX.
Some experiences I had 15 years ago with SOLARIS have been repeated
with LINUX.
I used this LD_DEBUG environment variable to see what the dynamic
linker is doing.
It looks like that even symbols needed in one shared library and
exported by the same shared library are bound at runtime!!!!
A colleague of mine had this experience debugging some shared
library,
doing some step into function which was located inside the same shared
library and actually ending up in some other shared library.
Taking into account, that every symbol exported from every object file
"bound" into a shared library is exported by this shared library!
I know -- I found this --version-script option -- but it is probably
quite undefined which symbols one is allowed to hide and which are
actually needed.
Did not find any documentation regarding this!

OK -- dlopen flags:

There is RTLD_GLOBAL.
This says, that symbols exported by the shared library can be used to
resolve undefined symbols from any other shared library.
Talk about random binding....

Then there is RTLD_LAZY -- to make it possible to successfully load a
shared library with undefined symbols (and crash when calling into
it).

Then there is RTLD_DEEPBIND which should not be optional -- seems to
be new though.

My experience boils out to the following:

I had some shared library A which was loaded via dlopen with
RTLD_GLOBAL.
This shared library A loaded another shared library B.
The shared library A was unloaded, which caused destructors to be
called to unload shared library B.
From what it looks like, this scenario does not go well together with
RTLD_GLOBAL.
Usually destructors should be called by dlclose().
In case of using RTLD_GLOBAL the library is unloaded with dlclose()
but the destructors are called only later when the program terminates
-- calling into the void.
I've a simple example program demonstrating this (output of "more *.c*
Makefile").....


::::::::::::::
c_main.c
::::::::::::::
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>


int c_main(int argc, char **argv)
{ void *const p = dlopen("/home/peterf/dlltest/libtest.so", RTLD_NOW |
RTLD_LOCAL |RTLD_DEEPBIND);
void (*pF)(void) = (void(*)(void))dlsym(p, "_test");
if (pF)
(*pF)();
if (p)
{ fprintf(stderr, "dlclose(libtest.so)\n");
dlclose(p);
}
else
fprintf(stderr, "%s\n", dlerror());
fprintf(stderr, "exit()\n");
exit(0);
return 0;
}
::::::::::::::
dll.cpp
::::::::::::::
#include <iostream>
#include <dlfcn.h>

extern "C" void __test(void);
struct test
{ void *p;
test(void)
{ std::cerr << "test()" << std::endl;
p = dlopen("/home/peterf/dlltest/libtest1.so", RTLD_NOW | RTLD_LOCAL
| RTLD_DEEPBIND);
if (p)
{ void (*pF)(void(*)(void)) = (void (*)(void (*)(void)))dlsym(p,
"_test1");
if (pF)
(*pF)(&__test);
}
}
~test(void)
{ std::cerr << "~test()" << std::endl;
std::cerr << "dlclose(libtest1.so)" << std::endl;
dlclose(p);
}
};


extern "C" void _test(void)
{ static test s;
}

extern "C" void __test(void)
{ std::cerr << "back in test..." << std::endl;
}
::::::::::::::
dll1.cpp
::::::::::::::
#include <iostream>
#include <dlfcn.h>

struct test1
{ test1(void)
{ std::cerr << "test1()" << std::endl;
}
~test1(void)
{ std::cerr << "~test1()" << std::endl;
}
};


//extern "C" void __test(void);
extern "C" void _test1(void (*_pF)(void))
{ static test1 s;
//__test();
(*_pF)();
}
::::::::::::::
main.cpp
::::::::::::::
#include <dlfcn.h>
#include <iostream>


extern "C" int c_main(int argc, char **argv);
int main(int argc, char **argv)
{ return c_main(argc, argv);
#if 0
void *const p = dlopen("/home/peterf/dlltest/libtest.so", RTLD_NOW |
RTLD_LOCAL);
void (*pF)(void) = (void(*)(void))dlsym(p, "_test");
if (pF)
(*pF)();
if (p)
{ std::cerr << "dlclose(libtest.so)" << std::endl;
dlclose(p);
}
else
std::cerr << dlerror() << std::endl;
std::cerr << "exit(0)" << std::endl;
//exit(0);
return 0;
#endif
}
::::::::::::::
Makefile
::::::::::::::
all: main.exe libtest.so libtest1.so


main.exe:main.cpp c_main.o
g++ -o $@ main.cpp c_main.o -g -ldl

libtest.so: dll.cpp
g++ -o $@ $< -shared -fPIC -g

libtest1.so: dll1.cpp
g++ -o $@ $< -shared -fPIC -g

c_main.o:c_main.c
gcc -c -g -o $@ $<

Peter

unread,
Jun 2, 2011, 8:28:03 PM6/2/11
to
On Jun 2, 4:40 pm, Peter <excessph...@gmail.com> wrote:


to reproduce the crash replace the RTLD_LOCAL in the c_main.c by
RTLD_GLOBAL
and the remove the RTLD_DEEPBIND.
Crash happens only in debug mode.
But also in release mode destructor ~test() is not called by
dlclose().
Only when using RTLD_LOCAL in main.c is the destructor call correct.

0 new messages