前文说到,可以用多种方法找到动态连接库中的导出函数,下面展示DeepSeek实现的两种调用方法。
方法一:用dlopen显式打开动态链接库文件,并用dlsym动态加载符号(需调用的函数)
#include <iostream>
#include <dlfcn.h>
#include <cstring>
typedef struct bignum_st BIGNUM;
typedef struct bn_ctx_st BN_CTX;
int main() {
// 加载动态库
void* lib = dlopen("libchdb.so", RTLD_LAZY);
if (!lib) {
std::cerr << "Error loading libchdb.so: " << dlerror() << std::endl;
return 1;
}
// 声明函数指针变量
BIGNUM* (*BN_new)() = nullptr;
void (*BN_free)(BIGNUM*) = nullptr;
int (*BN_dec2bn)(BIGNUM**, const char*) = nullptr;
char* (*BN_bn2dec)(const BIGNUM*) = nullptr;
// void (*OPENSSL_free)(void*) = nullptr;
int (*BN_add)(BIGNUM*, const BIGNUM*, const BIGNUM*) = nullptr;
int (*BN_mul)(BIGNUM*, const BIGNUM*, const BIGNUM*, BN_CTX*) = nullptr;
BN_CTX* (*BN_CTX_new)() = nullptr;
void (*BN_CTX_free)(BN_CTX*) = nullptr;
// 逐个加载符号并检查
if (!(BN_new = (decltype(BN_new))dlsym(lib, "BN_new"))) {
std::cerr << "Failed to load symbol: BN_new (" << dlerror() << ")" << std::endl;
}
else if (!(BN_free = (decltype(BN_free))dlsym(lib, "BN_free"))) {
std::cerr << "Failed to load symbol: BN_free (" << dlerror() << ")" << std::endl;
}
else if (!(BN_dec2bn = (decltype(BN_dec2bn))dlsym(lib, "BN_dec2bn"))) {
std::cerr << "Failed to load symbol: BN_dec2bn (" << dlerror() << ")" << std::endl;
}
else if (!(BN_bn2dec = (decltype(BN_bn2dec))dlsym(lib, "BN_bn2dec"))) {
std::cerr << "Failed to load symbol: BN_bn2dec (" << dlerror() << ")" << std::endl;
}
// else if (!(OPENSSL_free = (decltype(OPENSSL_free))dlsym(lib, "OPENSSL_free"))) {
// std::cerr << "Failed to load symbol: OPENSSL_free (" << dlerror() << ")" << std::endl;
// }
else if (!(BN_add = (decltype(BN_add))dlsym(lib, "BN_add"))) {
std::cerr << "Failed to load symbol: BN_add (" << dlerror() << ")" << std::endl;
}
else if (!(BN_mul = (decltype(BN_mul))dlsym(lib, "BN_mul"))) {
std::cerr << "Failed to load symbol: BN_mul (" << dlerror() << ")" << std::endl;
}
else if (!(BN_CTX_new = (decltype(BN_CTX_new))dlsym(lib, "BN_CTX_new"))) {
std::cerr << "Failed to load symbol: BN_CTX_new (" << dlerror() << ")" << std::endl;
}
else if (!(BN_CTX_free = (decltype(BN_CTX_free))dlsym(lib, "BN_CTX_free"))) {
std::cerr << "Failed to load symbol: BN_CTX_free (" << dlerror() << ")" << std::endl;
}
else {
// 所有符号加载成功,执行业务逻辑
BN_CTX* ctx = BN_CTX_new();
if (!ctx) {
std::cerr << "Error creating BN context" << std::endl;
dlclose(lib);
return 1;
}
BIGNUM* a = BN_new();
BIGNUM* b = BN_new();
BIGNUM* result = BN_new();
char* res_str = nullptr;
if (a && b && result) {
if (BN_dec2bn(&a, "12345678901234567890") && BN_dec2bn(&b, "98765432109876543210")) {
if (BN_add(result, a, b)) {
res_str = BN_bn2dec(result);
std::cout << "Addition result: " << res_str << std::endl;
// OPENSSL_free(res_str);
} else {
std::cerr << "BN_add operation failed" << std::endl;
}
if (BN_mul(result, a, b, ctx)) {
res_str = BN_bn2dec(result);
std::cout << "Multiplication result: " << res_str << std::endl;
// OPENSSL_free(res_str);
} else {
std::cerr << "BN_mul operation failed" << std::endl;
}
} else {
std::cerr << "Failed to parse numbers" << std::endl;
}
} else {
std::cerr << "Failed to create BIGNUMs" << std::endl;
}
// 清理资源
// if (res_str) OPENSSL_free(res_str);
if (a) BN_free(a);
if (b) BN_free(b);
if (result) BN_free(result);
if (ctx) BN_CTX_free(ctx);
}
// 关闭动态库
dlclose(lib);
return 0;
}
逐个加载符号并检查是为了确定不能加载的符号,便于排查,比如OPENSSL_free这个函数就不存在,需要去掉对它的调用。
编译命令行
g++ -o bn4 bn4.cpp -ldl
./bn4
Addition result: 111111111011111111100
Multiplication result: 1219326311370217952237463801111263526900
方法二:静态链接, 直接声明外部函数(假设这些函数在libchdb中已导出),因为这些函数与openssl中的函数同名,DeepSeek具有这方面的知识,所以它能正确声明。
#include <iostream>
#include <cstring>
// 声明BN函数(这些应该来自libchdb的头文件)
extern "C" {
typedef struct bignum_st BIGNUM;
typedef struct bn_ctx_st BN_CTX;
BIGNUM* BN_new();
void BN_free(BIGNUM*);
int BN_dec2bn(BIGNUM**, const char*);
char* BN_bn2dec(const BIGNUM*);
int BN_add(BIGNUM*, const BIGNUM*, const BIGNUM*);
int BN_mul(BIGNUM*, const BIGNUM*, const BIGNUM*, BN_CTX*);
BN_CTX* BN_CTX_new();
void BN_CTX_free(BN_CTX*);
// 如果OPENSSL_free不可用,可以用系统的free代替
void free(void*);
}
int main() {
// 初始化上下文
BN_CTX* ctx = BN_CTX_new();
if (!ctx) {
std::cerr << "Error creating BN context" << std::endl;
return 1;
}
// 创建并初始化大数
BIGNUM* a = BN_new();
BIGNUM* b = BN_new();
BIGNUM* result = BN_new();
char* res_str =nullptr;
if (!a || !b || !result) {
std::cerr << "Error creating BIGNUM" << std::endl;
goto cleanup;
}
// 设置十进制数值
if (!BN_dec2bn(&a, "12345678901234567890") || !BN_dec2bn(&b, "98765432109876543210")) {
std::cerr << "Error parsing numbers" << std::endl;
goto cleanup;
}
// 执行加法运算
if (!BN_add(result, a, b)) {
std::cerr << "Addition failed" << std::endl;
goto cleanup;
}
// 输出加法结果
res_str= BN_bn2dec(result);
std::cout << "Addition result: " << res_str << std::endl;
free(res_str); // 使用系统free代替OPENSSL_free
// 执行乘法运算
if (!BN_mul(result, a, b, ctx)) {
std::cerr << "Multiplication failed" << std::endl;
goto cleanup;
}
// 输出乘法结果
res_str = BN_bn2dec(result);
std::cout << "Multiplication result: " << res_str << std::endl;
free(res_str); // 使用系统free代替OPENSSL_free
cleanup:
// 释放资源
if (a) BN_free(a);
if (b) BN_free(b);
if (result) BN_free(result);
if (ctx) BN_CTX_free(ctx);
return 0;
}
编译命令行,需要提前设置环境变量,让它包含libchdb.so文件路径。
export LD_LIBRARY_PATH=/par:/usr/local/lib:/par/duck/build/src
export LIBRARY_PATH=/par:/usr/local/lib:/par/duck/build/src
g++ -o bn5 bn5.cpp -lchdb
./bn5
Addition result: 111111111011111111100
Multiplication result: 1219326311370217952237463801111263526900
这两种方法的执行速度差不多,主要时间都花在加载动态链接库上了。