运行时可以自己自定义so库的动态加载框架,主动去加载某些库,并调用其中的某些方法
首先写一些方法,并生成so库
// hello.cpp
#include
/*
使用 nm 命令查看 so 库的内容
*/
// 1. 使用extern
// dlsym(handle, "hello")
extern "C"
void hello() {
std::cout << "hello" << std::endl;
}
// 2. 不使用extern
// dlsym(handle, "_Z2hiv")
void hi() {
std::cout << "hi" << std::endl;
}
// _Z3addi
int add(int a) {
return a + 1;
}
编译为so库:
c++ -shared -std=c++11 hello.cpp -o hello.so
此时可以通过nm命令看看so库的内容
nm hello.so
0000000000003efc s GCC_except_table38
0000000000003f10 s GCC_except_table43
0000000000003e84 s GCC_except_table6
0000000000003ec4 s GCC_except_table7
0000000000003ed8 s GCC_except_table9
U __Unwind_Resume
00000000000032d0 T __Z2hiv
0000000000003300 T __Z3addi
0000000000003ab0 t __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE13__get_pointerB7v160006Ev
0000000000003b60 t __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE18__get_long_pointerB7v160006Ev
0000000000003b80 t __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE19__get_short_pointerB7v160006Ev
0000000000003920 t __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4dataB7v160006Ev
0000000000003b00 t __ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE9__is_longB7v160006Ev
0000000000003540 t __ZNKSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentrycvbB7v160006Ev
0000000000003bc0 t __ZNKSt3__117__compressed_pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repES5_E5firstB7v160006Ev
0000000000003840 t __ZNKSt3__119ostreambuf_iteratorIcNS_11char_traitsIcEEE6failedB7v160006Ev
0000000000003be0 t __ZNKSt3__122__compressed_pair_elemINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repELi0ELb0EE5__getB7v160006Ev
0000000000003d90 t __ZNKSt3__15ctypeIcE5widenB7v160006Ec
U __ZNKSt3__16locale9use_facetERNS0_2idE
00000000000037b0 t __ZNKSt3__18ios_base5flagsB7v160006Ev
0000000000003c80 t __ZNKSt3__18ios_base5rdbufB7v160006Ev
00000000000038a0 t __ZNKSt3__18ios_base5widthB7v160006Ev
U __ZNKSt3__18ios_base6getlocEv
00000000000037d0 t __ZNKSt3__19basic_iosIcNS_11char_traitsIcEEE4fillB7v160006Ev
0000000000003c60 t __ZNKSt3__19basic_iosIcNS_11char_traitsIcEEE5rdbufB7v160006Ev
0000000000003cd0 t __ZNKSt3__19basic_iosIcNS_11char_traitsIcEEE5widenB7v160006Ec
0000000000003ca0 T __ZNSt3__111char_traitsIcE11eq_int_typeEii
0000000000003cc0 T __ZNSt3__111char_traitsIcE3eofEv
0000000000003500 T __ZNSt3__111char_traitsIcE6lengthEPKc
0000000000003aa0 t __ZNSt3__112__to_addressB7v160006IKcEEPT_S3_
U __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEmc
00000000000038f0 t __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC1B7v160006Emc
0000000000003980 t __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEC2B7v160006Emc
U __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED1Ev
U __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc
U __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv
U __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_
U __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev
0000000000003260 t __ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsB7v160006EPFRS3_S4_E
0000000000003bf0 t __ZNSt3__114pointer_traitsIPKcE10pointer_toB7v160006ERS1_
00000000000038c0 t __ZNSt3__115basic_streambufIcNS_11char_traitsIcEEE5sputnB7v160006EPKcl
0000000000003a90 t __ZNSt3__116__non_trivial_ifILb1ENS_9allocatorIcEEEC2B7v160006Ev
0000000000003560 t __ZNSt3__116__pad_and_outputB7v160006IcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_
00000000000039d0 t __ZNSt3__117__compressed_pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repES5_EC1B7v160006INS_18__default_init_tagESA_EEOT_OT0_
0000000000003a10 t __ZNSt3__117__compressed_pairINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repES5_EC2B7v160006INS_18__default_init_tagESA_EEOT_OT0_
0000000000003df0 t __ZNSt3__118__constexpr_strlenB7v160006EPKc
0000000000003a00 t __ZNSt3__119__debug_db_insert_cB7v160006INS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEEEvPT_
0000000000003780 t __ZNSt3__119ostreambuf_iteratorIcNS_11char_traitsIcEEEC1B7v160006ERNS_13basic_ostreamIcS2_EE
0000000000003c00 t __ZNSt3__119ostreambuf_iteratorIcNS_11char_traitsIcEEEC2B7v160006ERNS_13basic_ostreamIcS2_EE
0000000000003a40 t __ZNSt3__122__compressed_pair_elemINS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE5__repELi0ELb0EEC2B7v160006ENS_18__default_init_tagE
0000000000003a50 t __ZNSt3__122__compressed_pair_elemINS_9allocatorIcEELi1ELb1EEC2B7v160006ENS_18__default_init_tagE
0000000000003310 t __ZNSt3__124__put_character_sequenceB7v160006IcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m
0000000000003bb0 t __ZNSt3__130__libcpp_is_constant_evaluatedB7v160006Ev
U __ZNSt3__14coutE
0000000000003280 t __ZNSt3__14endlB7v160006IcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_
U __ZNSt3__15ctypeIcE2idE
U __ZNSt3__16localeD1Ev
U __ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv
U __ZNSt3__18ios_base5clearEj
0000000000003950 t __ZNSt3__18ios_base5widthB7v160006El
0000000000003dc0 t __ZNSt3__18ios_base8setstateB7v160006Ej
0000000000003a70 t __ZNSt3__19allocatorIcEC2B7v160006Ev
0000000000003860 t __ZNSt3__19basic_iosIcNS_11char_traitsIcEEE8setstateB7v160006Ej
0000000000003d60 t __ZNSt3__19use_facetB7v160006INS_5ctypeIcEEEERKT_RKNS_6localeE
0000000000003220 t __ZNSt3__1lsB7v160006INS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
U __ZSt9terminatev
0000000000003890 t ___clang_call_terminate
U ___cxa_begin_catch
U ___cxa_call_unexpected
U ___cxa_end_catch
U ___gxx_personality_v0
00000000000031f0 T _hello
U _strlen
可以看到:
00000000000032d0 T __Z2hiv
0000000000003300 T __Z3addi
00000000000031f0 T _hello
U _strlen
动态加载示例代码,演示加载 so库(so库名为argv[1]),并从中读取1个函数(函数名为argv[2])
(假设该函数格式为 void(*)(void) )
#include
#include
#include
#include
/**
*
extern "C"
void hello() {
std::cout << "hello" << std::endl;
}
void hi() {
std::cout << "hi" << std::endl;
}
使用 nm xxx.so 查看函数表(去掉函数前的第一个下划线)
对于有 extern "C" 修饰的C++函数,dlsym(handle, "hello")
对于没有 extern "C" 修饰的C++函数,dlsym(handle, "_Z2hiv")
*
*/
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "dlsym " << std::endl;
return 0;
}
void* handle = dlopen(argv[1], RTLD_NOW);
if (handle == nullptr) {
std::cerr << "dlopen error: " << dlerror() << std::endl;
exit(-1);
}
dlerror();
// FIXME 这里仅演示获取 void(*)(void) 类型的函数
void (*func)(void) = (void(*)(void))dlsym(handle, argv[2]);
if (func == nullptr) {
std::cerr << "dlsym error: " << dlerror() << std::endl;
exit(-1);
}
// 调用查找到的函数
func();
int (*add)(int) = (int(*)(int))dlsym(handle, "_Z3addi");
int r = add(1008);
std::cout << "add: " << r << std::endl;
int res = dlclose(handle);
if (res == -1) {
std::cerr << "dlclose error: " << dlerror() << std::endl;
exit(-1);
}
return 0;
}
要注意原hello.so库中的函数实现,是否在方法前有extern “C”
若有疑问的,可以看官方文档 语言链接 – cppreference.com
此处执行3次,用来演示extern对于C++代码的影响:
./dlsym ./hello.so hello
hello
add: 1009
./dlsym ./hello.so hi
dlsym error: dlsym(0x7ff90d118010, hi): symbol not found
./dlsym ./hello.so _Z2hiv
hi
add: 1009
注意:
日常中使用动态加载方案时,一定要规范好so库的成果物,一定要事先确认是否使用extern “C”