vala-lang 如何访问动态链接函数?
作为参考示例,假设我们想要重现此 cpp openssl md5 程序](https://stackoverflow.com/a/73843621/1695680),除非使用动态加载的 libssl。
在下面的例子中,我们使用
dlopen
和 dlsym
打开
// vala-dlopen-openssl-md5sum.vala
// begin POSIX dlopen
[CCode (cname="dlopen")]
extern unowned void *dlopen(string filename, int flag);
[CCode (cname="dlerror")]
extern unowned string dlerror();
[CCode (cname="dlsym")]
extern unowned void *dlsym(void *handle, string symbol);
const int RTLD_LAZY = 1;
// end POSIX dlopen
// being openssl
delegate unowned void *EVP_MD_CTX_new();
delegate unowned void *EVP_md5();
delegate unowned int EVP_DigestInit_ex2(void *ctx, void *md, void* params);
delegate unowned int EVP_DigestUpdate(void *ctx, void *d, ulong cnt);
delegate unowned int EVP_DigestFinal_ex(void *ctx, uchar *md, uint *s);
// end openssl
int main(string[] args) {
// begin dlopen
var openssl = dlopen("libssl.so", RTLD_LAZY);
if (openssl == null) {
stderr.printf("dlopen failed: %s\n", dlerror());
return 2;
}
var fn_EVP_MD_CTX_new = (EVP_MD_CTX_new) dlsym(openssl, "EVP_MD_CTX_new");
if (fn_EVP_MD_CTX_new == null) {
stderr.printf("dlsym failed: %s\n", dlerror());
return 2;
}
var fn_EVP_md5 = (EVP_md5) dlsym(openssl, "EVP_md5");
if (fn_EVP_md5 == null) {
stderr.printf("dlsym failed: %s\n", dlerror());
return 2;
}
var fn_EVP_DigestInit_ex2 = (EVP_DigestInit_ex2) dlsym(openssl, "EVP_DigestInit_ex2");
if (fn_EVP_DigestInit_ex2 == null) {
stderr.printf("dlsym failed: %s\n", dlerror());
return 2;
}
var fn_EVP_DigestUpdate = (EVP_DigestUpdate) dlsym(openssl, "EVP_DigestUpdate");
if (fn_EVP_DigestUpdate == null) {
stderr.printf("dlsym failed: %s\n", dlerror());
return 2;
}
var fn_EVP_DigestFinal_ex = (EVP_DigestFinal_ex) dlsym(openssl, "EVP_DigestFinal_ex");
if (fn_EVP_DigestFinal_ex == null) {
stderr.printf("dlsym failed: %s\n", dlerror());
return 2;
}
// end dlopen
// begin ms5sum example
void *ctx = fn_EVP_MD_CTX_new();
void *md = fn_EVP_md5();
uchar md_value[33];
md_value = { 0 };
uint md_len = 0;
fn_EVP_DigestInit_ex2(ctx, md, null);
// example-md5sum a string (argv 1)
// vala ./vala-dlopen-openssl-md5sum.vala --run-args "hello world"
string subject = args[1];
if (subject != null) {
char []content = subject.to_utf8();
fn_EVP_DigestUpdate(ctx, content, content.length);
// example-md5sum a stream (stdin)
// echo "hello world" | vala ./vala-dlopen-openssl-md5sum.vala
} else {
while (!stdin.eof()) {
uint8[] buf = new uint8[40960];
size_t buf_size = stdin.read (buf, 40960);
fn_EVP_DigestUpdate(ctx, buf, buf_size);
}
}
fn_EVP_DigestFinal_ex(ctx, md_value, &md_len);
// digest to hex
string output = "";
for (uint i = 0; i < md_len; ++i) {
output = "%s%02x".printf(output, md_value[i]);
}
// end ms5sum example
stdout.printf("%s\n", output);
return 0;
}
我在这个论坛帖子中发现了一个与 dlopen/dlsym 交互的例子:https://www.rocksaying.tw/archives/14551049.html
包含的示例依赖于一个神秘的 ./md5sum.dll,但涉及与 dlopen/dlsym 交互的 vala 代码似乎确实有效,从那里我能够将此示例改编为工作的 openssl md5sum 客户端,并将 md5sum 保留为简单的“你好世界”示例。
值得注意的是,在这个例子中,我们凭空拉出
dlopen
和 dlsym
,大概它们链接是因为 POSIX 标准或包含的 libc 指定它们将存在。
如果我们没有特别需要动态链接,我们当然可以使用 vala-api/.vapi 文件包装目标 c 库,并跳过
delegate
声明并将函数指针类型转换为委托类型。