dlopen 和函数属性 __attribute__((constructor))& __attribute__((destructor))

本文介绍如何使用__attribute__((constructor))和__attribute__((destructor))在共享对象加载和卸载时执行特定函数。通过GCC编译器支持的属性,可以在dlopen前调用初始化函数,在dlclose前调用清理函数。
Shared objects may export functions using the

__attribute__((constructor)) and __attribute__((destructor))
function attributes.  Constructor functions are executed before
dlopen() returns, and destructor functions are executed before
dlclose() returns.  A shared object may export multiple
constructors and destructors, and priorities can be associated
with each function to determine the order in which they are
executed.  See the gcc info pages (under "Function attributes")
for further information.

函数设置__attribute__((constructor))属性,在dlopen时,会先调用该方法。

设置__attribute__((destructor))属性,在dlclose时会调用该方法。

demo

下面举例

// test.c
#include <stdio.h>

__attribute__((constructor)) void test_init() {
    printf("hello world\n");
}

__attribute__((destructor)) void test_fini() {
    printf("bye world\n");
}

void test() {
    printf("test\n");
}

gcc -fPIC -shared test.c -o  libtest.so
#include <stdio.h>
#include <dlfcn.h>

typedef void(*testFunc)();
int main() {
    void *handle = dlopen("./libtest.so", RTLD_LAZY);
    if (handle == NULL) {
        printf("handle is null, %s\n", dlerror());
    }
    printf("----------\n");
    testFunc f = (testFunc)dlsym(handle, "test");
    if (f == NULL) {
        printf("f is null\n");
    }

    f();
    dlclose(handle);
    return 0;
}
gcc main.c -ldl -O0 -g

执行可执行文件,在dlopen时执行test_init方法,最后在dlclose执行test_fini方法。

// a.out
hello world
----------
test
bye world
在使用 PAM(可插拔认证模块)时,如果遇到 `PAM error: unable to dlopen EVP_KDF_ctrl` 错误,通常与动态链接库加载失败或函数符号缺失有关。以下是对该问题的详细分析及可能的解决方案: ### 1. **EVP_KDF_ctrl 函数的来源** `EVP_KDF_ctrl` 是 OpenSSL 提供的一个函数,属于 EVP(Envelope Encryption)接口的一部分,用于密钥派生函数(KDF)的控制操作。该函数通常在 OpenSSL 的 `libcrypto` 库中定义,版本要求为 OpenSSL 1.1.1 或更高版本。如果系统中使用的 OpenSSL 版本较低,可能导致该函数不可用,从而引发 PAM 模块加载失败的问题[^5]。 ### 2. **动态链接库加载失败** PAM 模块通常是动态链接库文件(如 `.so` 文件),在运行时通过 `dlopen()` 函数加载。如果 PAM 模块依赖的库(如 `libcrypto.so`)路径不正确、版本不兼容或缺失,会导致 `dlopen()` 调用失败,进而引发 `unable to dlopen` 错误。可以通过以下命令检查 PAM 模块的依赖关系: ```bash ldd /lib/security/pam_module_name.so ``` 确保所有依赖库都正确解析,并且版本兼容。 ### 3. **函数符号缺失** 如果 OpenSSL 库版本过低,缺少 `EVP_KDF_ctrl` 函数,PAM 模块在加载时无法找到该符号,从而导致错误。可以通过以下方式检查 OpenSSL 版本及是否包含该函数: ```bash openssl version nm -D /usr/lib64/libcrypto.so | grep EVP_KDF_ctrl ``` 如果输出中没有 `EVP_KDF_ctrl`,说明当前 OpenSSL 版本不支持该函数,需要升级 OpenSSL 到 1.1.1 或更高版本。 ### 4. **配置文件错误** PAM 模块的配置文件(如 `/etc/pam.d/vsftpd.mysql` 或 `/etc/security/pam_mysql.conf`)中可能存在错误的参数或路径配置,导致模块加载失败。例如,如果 `pam_mysql` 模块的配置文件中指定的 `config_file` 路径错误,或配置文件中参数格式不正确,也可能引发 `unable to dlopen` 错误。请检查配置文件内容,确保路径参数格式正确[^2]。 ### 5. **文件权限问题** PAM 模块及其依赖库的文件权限设置不当也可能导致加载失败。例如,模块文件或其依赖库的权限过于严格,导致运行 PAM 的进程无法读取。可以通过以下命令检查文件权限: ```bash ls -l /lib/security/pam_module_name.so ls -l /usr/lib64/libcrypto.so ``` 确保运行 PAM 的进程具有读取权限。 ### 6. **系统完整性问题** 在某些情况下,系统文件(如 `/etc/pam.d` 目录或相关库文件)可能被意外删除或损坏,导致 PAM 模块无法正常加载。例如,引用中提到 `/etc/pam.d` 目录丢失的情况,可能导致 PAM 认证失败。可以通过从其他相同系统的备份中恢复相关文件来解决此类问题[^4]。 ### 7. **解决方案** - **升级 OpenSSL**:确保系统中安装的 OpenSSL 版本为 1.1.1 或更高版本,以支持 `EVP_KDF_ctrl` 函数。 - **检查依赖库**:使用 `ldd` 命令检查 PAM 模块的依赖库是否正确解析。 - **验证配置文件**:检查 PAM 模块的配置文件,确保路径参数格式正确。 - **修复文件权限**:确保 PAM 模块及其依赖库的文件权限允许运行 PAM 的进程读取。 - **恢复系统文件**:如果系统文件(如 `/etc/pam.d` 目录)丢失或损坏,可以从备份中恢复。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值