动态加载so库

动态加载so库

前置条件

#include <iostream>
extern "C"
void hello(){
    std::cout << "Hello World from share library!\n";
}

编译动态库

g++ -fPIC -shared hello.cpp -o libhello.so

如果没有extern "C",则会生成类似_Z5hellov的符号

修饰后的字符 含义解读
_Z 所有 C++ 函数修饰的固定前缀(标识这是一个 C++ 符号)
5 紧跟其后的函数名长度(hello 由 5 个字符组成:h-e-l-l-o)
hello 原始函数名
v 函数参数类型编码(v 对应 void,表示函数无参数)

nm

nm -DCU libhello.so
# 0000000000001119 T hello

nm的作用时从二进制目标文件中列出符号表.
-D:用于显示动态符号而非普通符号
-C: --demangle[=STYLE] 解码经过名字修饰(mangled)或处理后的符号名
STYLE 可选值:"none"(不解码)、"auto"(自动检测)、"gnu-v3"(GNU v3 标准)、
"java"(Java 格式)、"gnat"(GNAT 编译器格式)、"dlang"(D 语言格式)、"rust"(Rust 语言格式)
--no-demangle 不解码底层符号名
--recurse-limit 启用名字解码的递归限制(默认启用)
--no-recurse-limit 禁用名字解码的递归限制
-U: Display only defined symbols

readelf

顾名思义,这个命令的含义就是读取elf文件结构的命令。
-h: 打印elf文件的头信息。
-S: 打印段表
-s: 打印符号表
-d: 打印动态依赖

动态加载

#include <dlfcn.h>

#include <format>
#include <iostream>
#include <sstream>
#include <vector>

int main() {
    void* handle = dlopen("../libhello.so", RTLD_LAZY);
    if (handle == nullptr) {
        throw std::runtime_error("handle == nullptr");
    }
    dlerror();
    typedef void (*helloFunc)(); // 函数指针名称可以随意定义
    helloFunc hello = (helloFunc)dlsym(handle, "hello");
    const char* error = dlerror();
    if (error != nullptr) {
        std::cerr << "dlsym error: " << error << "\n";
        dlclose(handle);
        throw std::runtime_error(std::format("dlsym error: {}", error));
    }
    hello();
    dlclose(handle);
    return 0;
}

编译运行

g++ main.cpp -ldl -o main
./main

总体分为三步:

  1. 打开共享库dlopen
  2. 查找符号地址dlsym
  3. 调用函数
posted @ 2025-11-08 10:10  爱情丶眨眼而去  阅读(3)  评论(0)    收藏  举报