UPD (SOLVED): This issue has been solved by this - In short, the case CMAKE will produce visiable UNIQUE objects, and either -fvisibility=hidden
(then add attribute visibility=default manually) or --no-gnu-unique
can avoid the problem.
I found this when importing a large project into myselfs, and the following codes should be a minimal demo to illustrate it:
/* ---- header.h ---- */
struct A {
typedef (void (*func_t)();
static void empty() {}
static constexpr func_t funcs[1] = {empty};
const func_t *func_list;
A() { func_list = funcs; }
};
struct B {
B();
};
struct X {
A a;
B b;
};
/* ----- header.cpp ----- */
#include "header.h"
B::B() {}
/* ----- main.cpp ----- */
#include "header.h"
extern "C" {
void test() {
auto x = new X;
delete x;
// print(version_nubmer);
}
}
The library is built from CMake
add_library(main SHARED main.cpp header.cpp)
target_compile_options(main PRIVATE -fPIC)
Then I use a program that sequentially calls dlopen(); dlsym(test)(); dlclose(); dlopen(); dlsym(test)();
on this library (with RTLD_LAZY | RTLZ_LOCAL
), where before the second dlopen()
,
I change the version number to a different value, expecting it print the updated one. But, it doesn't. I still see it give the old value, which means dlclose()
does not really detach the lib.
If I set CMAKE_INTERPROCEDURAL_OPTIMIZATION to ON, or manually build it from command line (e.g., g++ -o libmain.so -fPIC -shared main.cpp header.cpp
) the observation disapears. Can anyone tell the difference here?
UPD: Between the dlclose() and second dlopen(), we insert something to block the program like scanf() or getchar(), and continue the program only after we rebuilt the lib. This will ensure the reproduction to the three cases (CMAKE, CMAKE+lto, cmd).