好记性不如铅笔头

linux, 操作系统

【转】采用dlopen、dlsym、dlclose加载动态链接库

本文转至【 https://www.cnblogs.com/Anker/p/3746802.html 】,有修改。

CONTENTS

生产动态链接库

编译参数 gcc -fPIC -shared 

将如下程序编译为动态链接库libcaculate.so,程序如下:

int add(int a,int b)
{
    return (a + b);
}

int sub(int a, int b)
{
    return (a - b);
}

int mul(int a, int b)
{
    return (a * b);
}

int div(int a, int b)
{
    return (a / b);
}

编译如下: gcc -fPIC -shared caculate.c -o libcaculate.so 

dlopen、dlsym函数介绍

在linux上man dlopen可以看到使用说明,函数声明如下:

#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);

dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程,dlerror返回出现的错误,dlsym通过句柄和连接符名称获取函数名或者变量名,dlclose来卸载打开的库。 

dlopen打开模式如下:
RTLD_LAZY 暂缓决定,等有需要时再解出符号 
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
flags 也可以通过以下零或多个值进行或运算设置:
RTLD_GLOBAL此共享对象定义的符号将可用于后续加载的共享对象的符号解析。
RTLD_LOCAL这与RTLD_GLOBAL相反,如果未指定任何标志,则为默认值。此共享对象中定义的符号不可用于解析后续加载的共享对象中的引用。
RTLD_NODELETE (since glibc 2.2)在dlclose()期间不要卸载共享对象。因此,如果稍后使用dlopen()重新加载对象,则不会重新初始化对象的静态变量。
RTLD_NOLOAD (since glibc 2.2)不要加载共享对象。这可用于测试对象是否已经驻留(如果不是,则dlopen()返回 NULL,如果是驻留则返回对象的句柄)。此标志还可用于提升已加载的共享对象上的标志。例如,以前使用RTLD_LOCAL加载的共享对象可以使用RTLD_NOLOAD | RTLD_GLOBAL重新打开。
RTLD_DEEPBIND (since glibc 2.3.4)将符号的查找范围放在此共享对象的全局范围之前。这意味着自包含对象将优先使用自己的符号,而不是全局符号,这些符号包含在已加载的对象中。
采用上面生成的libcaculate.so,写个测试程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

//动态链接库路径
#define LIB_CACULATE_PATH "./libcaculate.so"

//函数指针
typedef int (*CAC_FUNC)(int, int);

int main()
{
    void *handle;
    char *error;
    CAC_FUNC cac_func = NULL;

    //打开动态链接库
    handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY);
    if (!handle) {
    fprintf(stderr, "%s\n", dlerror());
    exit(EXIT_FAILURE);
    }

    //清除之前存在的错误
    dlerror();

    //获取一个函数
    *(void **) (&cac_func) = dlsym(handle, "add");
    if ((error = dlerror()) != NULL)  {
    fprintf(stderr, "%s\n", error);
    exit(EXIT_FAILURE);
    }
    printf("add: %d\n", (*cac_func)(2,7));

    cac_func = (CAC_FUNC)dlsym(handle, "sub");
    printf("sub: %d\n", cac_func(9,2));

    cac_func = (CAC_FUNC)dlsym(handle, "mul");
    printf("mul: %d\n", cac_func(3,2));

    cac_func = (CAC_FUNC)dlsym(handle, "div");
    printf("div: %d\n", cac_func(8,2));

    //关闭动态链接库
    dlclose(handle);
    exit(EXIT_SUCCESS);
}

编译选项如下:gcc -rdynamic -o main main.c -ldl


参考资料:
http://blog.chinaunix.net/uid-26285146-id-3262288.html
http://www.360doc.com/content/10/1213/22/4947005_77867631.shtml


最后附上dlsym上比较有意思的2个handler的说明:

There are two special pseudo-handles that may be specified in handle:
RTLD_DEFAULT
    Find the first occurrence of the desired symbol using the default shared object search order.  The search will include global symbols in the executable and its dependencies, as well as symbols in shared objects that were dynamically loaded with the RTLD_GLOBAL flag.

RTLD_NEXT
    Find  the next occurrence of the desired symbol in the search order after the current object.  This allows one to provide a wrapper around a function in another shared object, so that, for example, the definition of a function in a preloaded shared object (see LD_PRELOAD in ld.so(8)) can find and invoke the "real" function provided in another shared  object  (or  for that matter, the "next" definition of the function in cases where there are multiple layers of preloading).

The _GNU_SOURCE feature test macro must be defined in order to obtain the definitions of RTLD_DEFAULT and RTLD_NEXT from <dlfcn.h>.

 

发表评论

1 × 1 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据