【POSIX】运行时so库动态加载

运行时可以自己自定义so库的动态加载框架,主动去加载某些库,并调用其中的某些方法

首先写一些方法,并生成so库

cpp 复制代码
// hello.cpp

#include <iostream>

/*
 使用 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库:

cpp 复制代码
c++ -shared -std=c++11 hello.cpp -o hello.so

此时可以通过nm命令看看so库的内容

cpp 复制代码
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

可以看到:

cpp 复制代码
00000000000032d0 T __Z2hiv
0000000000003300 T __Z3addi

00000000000031f0 T _hello
                 U _strlen

动态加载示例代码,演示加载 so库(so库名为argv[1]),并从中读取1个函数(函数名为argv[2])

(假设该函数格式为 void(*)(void) )

cpp 复制代码
#include <dlfcn.h>
#include <iostream>
#include <unistd.h>
#include <string.h>

/**
 *  
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 <so-path> <func-name>" << 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++代码的影响:

cpp 复制代码
./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"

相关推荐
LeoLei80608 分钟前
新特性之C++17
开发语言·c++
小小怪下士的编程小屋35 分钟前
stm32中断
c语言·stm32·单片机·嵌入式硬件
jllws137 分钟前
C++基础:STL概述
开发语言·c++
FightingLod41 分钟前
C++中list容器使用详解
开发语言·c++·list
yyqzjw1 小时前
【C++】单例模式
c++·单例模式
每天的积累1 小时前
C++学习笔记二
c++·笔记·学习
zengy51 小时前
代码随想录打卡第十三天
数据结构·c++·算法·leetcode
Danica~2 小时前
RpcChannel的调用过程
网络·c++·rpc
结衣结衣.2 小时前
完全理解C语言函数
java·linux·c语言·数据库·经验分享·笔记
C_player_0012 小时前
C++ list的模拟实现
c++