ros / class_loader

ROS-independent library for dynamic class (i.e. plugin) introspection and loading from runtime libraries
http://www.ros.org/wiki/class_loader
34 stars 95 forks source link

Notes on `RTLD_GLOBAL` in the code ? #172

Open daohu527 opened 3 years ago

daohu527 commented 3 years ago

I found below notes in code. It say RTLD_GLOBAL will cause static global variable initialization problem when reopen the library.

I try load and unload the library, and the static variable is int correct. I don't know under what circumstances the problem will occur ? In fact, I found that some static variables in template classes and inline functions will cause this problem. I did not find these situations in the code.

https://github.com/ros/class_loader/blob/8864be372a83921f15906ae80752be9d6a300b81/src/class_loader_core.cpp#L235-L244

I found the RTLD_NODELETE dlopen doc, dlopen not use the RTLD_NODELETE option, I also make some test that static variable inited correctly.

RTLD_NODELETE (since glibc 2.2) Do not unload the library during dlclose(). Consequently, the library's static variables are not reinitialized if the library is reloaded with dlopen() at a later time. This flag is not specified in POSIX.1-2001.

hidmic commented 3 years ago

What are you proposing @daohu527 ? IIUC RTLD_GLOBAL is used to ensure RTTI (and dynamic_cast) works, but it also means it won't get unloaded if a symbol within the executable space depends on a symbol in that loaded shared library. That why it goes to a "graveyard".

daohu527 commented 3 years ago

I mean that even if dlopen sets RTLD_GLOBAL, it will be dynamically unloaded, including static variables.

We are divided into 2 situations to discuss

1 case

Some static variable with STB_GNU_UNIQUE will still in memory when call dlclose. you can ref to Destructor of a global static variable in a shared library is not called on dlclose.

There is not this case in below register code. (static variables in template classes and inline functions) https://github.com/ros/class_loader/blob/c012f7775881255e8f8a4c40f01f21469e0f472c/include/class_loader/register_macro.hpp#L41-L52

2 case

Instructions in the man manual dlopen

not this case too.

RTLD_NODELETE (since glibc 2.2) Do not unload the shared object during dlclose(). Consequently, the object's static and global variables are not reinitialized if the object is reloaded with dlopen() at a later time.

The following two descriptions may appear, unless the ros module can be referenced by ros module. we will explain in next subsection.

The dynamic linker maintains reference counts for object handles, so a dynamically loaded shared object is not deallocated until dlclose() has been called on it as many times as dlopen() has succeeded on it.

Symbols in this object might be required in another object because this object was opened with the RTLD_GLOBAL flag and one of its symbols satisfied a relocation in another object.

example

If A is an ros module and C is references A.

A       // A is a ros module
C <- A  // C references A

We first load A and then load C, the A module will Implicit load only once (reference counts is 2) and register once, then if we unload the library A, the A will still in memory, because the dlopen reference counts is 1, after that we load A again, and A will not register because A is already in memory.

reason

So the core principle is, can ROS modules be referenced by other ROS modules? But it is unreasonable to say that RTLD_GLOBAL causes this problem.