动态加载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
总体分为三步:
- 打开共享库
dlopen - 查找符号地址
dlsym - 调用函数

浙公网安备 33010602011771号