使用dlsym获取函数赋值给std::function导致段错误
问题分析与解决方案
首先,咱们直接定位触发段错误的核心原因——你在创建std::function之前就调用了dlclose(handle),这会直接卸载共享库,此时function指针已经变成了悬空指针,std::function尝试引用这个无效指针时,自然会触发段错误。
另外,你的代码还有一个潜在的未定义行为:extern "C"修饰的函数使用了C++专属类型std::array<double, 1>作为参数。extern "C"是让函数按照C语言的调用约定和命名规则处理,但C语言并不认识std::array,这会导致函数的ABI(应用二进制接口)不兼容,后续调用可能出现各种奇怪问题。
修复步骤
1. 修正共享库中的函数定义
把std::array替换为C兼容的类型,比如直接用double(因为你的数组长度固定为1),如果需要支持更长数组,可以用double*加长度参数。修改后的function.cpp如下:
#include <array> #ifdef __cplusplus extern "C" { #endif // 改用C兼容的double类型作为参数 double function(double arg) { return arg * 2; } #ifdef __cplusplus } #endif
2. 调整主程序的执行流程
将dlclose(handle)移到std::function使用完成之后,确保共享库在函数指针被引用期间保持加载状态。修改后的main.cpp如下:
#include <functional> #include <iostream> #include <fstream> #include <array> #include <cstdlib> // 补充exit需要的头文件 #ifdef __linux__ #include <dlfcn.h> #endif int main() { void *handle; // 对应修改函数指针的类型 double (*function)(double); char *error; handle = dlopen("/home/oleg/MyProjects/shared_library_test/libFunction.so", RTLD_LAZY); if (!handle) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } dlerror(); *(void **) (&function) = dlsym(handle, "function"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); } std::cout << "Native function output: " << function(3.0) << std::endl; // 先创建std::function并调用,再关闭共享库 std::function<double(double)> std_function(function); std::cout << "std::function output: " << std_function(3.0) << std::endl; // 现在可以安全关闭库了 dlclose(handle); exit(EXIT_SUCCESS); }
3. 重新编译运行
保持原来的编译命令不变:
编译共享库:
g++ -Wall -Wextra -g -std=c++17 -shared -o libFunction.so -fPIC function.cpp
编译主程序:
g++ -Wall -Wextra -g -std=c++17 main.cpp -ldl
运行后应该会正常输出:
Native function output: 6 std::function output: 6
额外说明
如果你的业务场景需要std::function长期持有函数指针(比如在库关闭后仍要使用),可以在dlopen时添加RTLD_NODELETE标志,这样即使调用dlclose,库也不会被真正卸载,直到进程退出:
handle = dlopen("/home/oleg/MyProjects/shared_library_test/libFunction.so", RTLD_LAZY | RTLD_NODELETE);
不过这种方式要注意内存占用,建议仅在必要时使用。
内容的提问来源于stack exchange,提问作者Oleg Yablokov
相关产品推荐
相关产品推荐

