dlclose后重新dlopen修改后的CMake共享库仍加载旧版本问题
动态库卸载异常问题分析与解决
更新(已解决)
该问题已解决——简言之,CMake会生成可见的UNIQUE对象,使用-fvisibility=hidden(需手动为必要符号添加visibility=default属性)或--no-gnu-unique编译选项可规避该问题。
问题背景与最小复现代码
在引入大型项目时发现动态库无法被dlclose()真正卸载的问题,以下是最小复现代码:
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); } }
构建脚本
该库通过CMake构建,配置如下:
add_library(main SHARED main.cpp header.cpp) target_compile_options(main PRIVATE -fPIC)
测试流程
编写程序依次执行以下操作:
dlopen()加载动态库(参数为RTLD_LAZY | RTLD_LOCAL)- 通过
dlsym()获取test函数并调用 - 调用
dlclose()卸载库 - 修改库的版本号后重新编译
- 再次执行
dlopen()加载库、dlsym()调用test函数
原本期望第二次加载后打印更新后的版本号,但实际仍输出旧值,说明dlclose()并未真正卸载动态库。
不同构建方式的差异
若开启CMAKE_INTERPROCEDURAL_OPTIMIZATION(即LTO优化),或直接通过命令行编译(如g++ -o libmain.so -fPIC -shared main.cpp header.cpp),上述动态库无法卸载的现象会消失。请问这几种构建方式的核心差异是什么?
更新
在dlclose()和第二次dlopen()之间插入scanf()或getchar()阻塞程序,待重编译库后再继续执行,可确保三种场景(普通CMake构建、CMake+LTO构建、命令行构建)都能复现该问题。
内容的提问来源于stack exchange,提问作者Yuxai
相关产品推荐
相关产品推荐

