c++动态库调用外部函数汇总。

1,基本使用动态库示例

#include <stdio.h>

int func_in_lib(int k)
{
    printf("func_in_lib is called 
");
    return k + 1;
}

[clear#] gcc -fPIC --shared -o lib.so lib.c

如上的lib.so库,可直接引用动态库即可。

使用系统方法条用动态库:

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

typedef int (*pfunc)(int);

void func_in_main(void)
{
    printf("func_in_main 
");
}

int main(int argc, char *agv[])
{
    int a = 1;
    int b;

    // 打开动态库
    void *handle = dlopen("./lib.so", RTLD_NOW);
    if (handle)
    {
        // 查找动态库中的函数
        pfunc func = (pfunc) dlsym(handle, "func_in_lib");
        if (func)
        {
            b = func(a);
            printf("b = %d 
", b);
        }
        else
        {
            printf("dlsym failed! 
");
        }
        dlclose(handle);
    }
    else
    {
        printf("dlopen failed! 
");
    }
    
    return 0;
}

[root]# ./main func_in_lib is called b = 2

如上调用没有任何问题,一切正常。

如果需要再动态库中调用外部的函数,需要注意的几点,总结如下,如下例子:

#include <stdio.h>
// 外部函数声明
void func_in_main(void);

int func_in_lib(int k)
{
    printf("func_in_lib is called 
");
    func_in_main();
    return k + 1;
}

如上示例,在库中声明一个函数,并调用,外部函数实现外部。

此刻之前的main并没有做任何修改,直接运行main程序,结果如下:

[root@#] ./main

dlopen failed!

运行打开库失败了。

原因:动态库引用了外部函数,仅仅只是在库中进行了声明,但是使用者并未制定外部函数的实现,没有指定对应函数地址,动态库执行中因找不到导致报错。

解决方法:需要将外部函数的符号倒进去,并且main在自身内提供一个func_in_main方法的实现,编译时需要将该方法的符号地址倒入。如下几个案列:

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

typedef int (*pfunc)(int);

void func_in_main(void)
{
    printf("func_in_main 
");
}

int main(int argc, char *agv[])
{
    int a = 1;
    int b;

    // 打开动态库
    void *handle = dlopen("./lib.so", RTLD_NOW);
    if (handle)
    {
        // 查找动态库中的函数
        pfunc func = (pfunc) dlsym(handle, "func_in_lib");
        if (func)
        {
            b = func(a);
            printf("b = %d 
", b);
        }
        else
        {
            printf("dlsym failed! 
");
        }
        dlclose(handle);
    }
    else
    {
        printf("dlopen failed! 
");
    }
    
    return 0;
}

[root@dynamic]# gcc -m32 -fPIC --shared -o lib.so lib.c [root@dynamic]# gcc -g -o main main.c -ldl

[root@dynamic]# ./main dlopen failed!

如上虽然提供了外部函数的实现,编译程序时因没有将外部函数的符号地址倒入,执行时动态库仍旧找不到,所以就撂挑子不干啦。

查看导出的符号表 [root@/data/myC++/dynamic]# $ objdump -e main -T | grep func_in_main [root@/data/myC++/dynamic]# # 这里输出为空

因为如上仅仅是在可执行程序添加了外部函数,func_in_main函数符号并没倒出来。

修改如上编译:

<1>修改外部符号导出-所有符号:

[root#dynamic]gcc -m32 -rdynamic -o main main.c -ldl

[root#dynamic] ./main

func_in_lib is called func_in_main b = 2

<2>将指定符号罗列出来,

export.txt罗列需要倒出的方法符号:

{ extern "C" { func_in_main; }; };

[root@dynamic]# gcc -m32 -Wl,-dynamic-list=./exported.txt -o main main.c -ldl

[root@dynamic] ./main

func_in_lib is called func_in_main b = 2

以上两种方法,即可即决,动态库调用外部函数处理方式。

2.变更修改方式:

a.在动态库中可以将自己需要被执行的函数注册到动态库中,可以设置默认值。如下:

lib.c

#include <stdio.h>

// 默认实现
void func_in_main_def(void)
{
    printf("the main is lazy, do NOT register me! 
");
}

// 定义外部函数指针
void (*func_in_main)() = func_in_main_def;

void register_func(void (*pf)())
{
    func_in_main = pf;
}

int func_in_lib(int k)
{
    printf("func_in_lib is called 
");

    if (func_in_main)
        func_in_main();

    return k + 1;
}

main.c

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

typedef int (*pfunc)(int);
typedef int (*pregister)(void (*)());

// 控制注册函数的宏定义
#define REG_FUNC

#ifdef REG_FUNC
void func_in_main(void)
{
    printf("func_in_main 
");
}
#endif

int main(int argc, char *agv[])
{
    int a = 1;
    int b;

    // 打开动态库
    void *handle = dlopen("./lib.so", RTLD_NOW);
    if (handle)
    {
#ifdef REG_FUNC
        // 查找动态库中的注册函数
        pregister register_func = (pregister) dlsym(handle, "register_func");
        if (register_func)
        {

            register_func(func_in_main);
        }
#endif

        // 查找动态库中的函数
        pfunc func = (pfunc) dlsym(handle, "func_in_lib");
        if (func)
        {
            b = func(a);
            printf("b = %d 
", b);
        }
        else
        {
            printf("dlsym failed! 
");
        }
        dlclose(handle);
    }
    else
    {
        printf("dlopen failed! 
");
    }
    
    return 0;
}

如上使用dlopen调用,执行如下结果:

[root@/myC++/dynamic]# gcc -m32 -o main main.c -ldl [root@/myC++/dynamic]# ./main func_in_lib is called the main is lazy, do NOT register me! b = 2

如上提示:the main is lazy, do NOT register me! ,因为没有主动注册,可以忽略,如果不想出现提示,就注册一个函数即可。

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

typedef int (*pfunc)(int);
typedef int (*pregister)(void (*)());

// 控制注册函数的宏定义
#define REG_FUNC

#ifdef REG_FUNC
void func_in_main(void)
{
    printf("func_in_main 
");
}
#endif

int main(int argc, char *agv[])
{
    int a = 1;
    int b;

    // 打开动态库
    void *handle = dlopen("./lib.so", RTLD_NOW);
    if (handle)
    {
#ifdef REG_FUNC
        // 查找动态库中的注册函数
        pregister register_func = (pregister) dlsym(handle, "register_func");
        if (register_func)
        {

            register_func(func_in_main);
        }
#endif

        // 查找动态库中的函数
        pfunc func = (pfunc) dlsym(handle, "func_in_lib");
        if (func)
        {
            b = func(a);
            printf("b = %d 
", b);
        }
        else
        {
            printf("dlsym failed! 
");
        }
        dlclose(handle);
    }
    else
    {
        printf("dlopen failed! 
");
    }
    
    return 0;
}
经验分享 程序员 微信小程序 职场和发展