0
点赞
收藏
分享

微信扫一扫

编译小结(3) 动态库(.so)编译及二种调用技巧


  动态库的概念和优势在这就不多说了,这里只说编译和调用。
下面会一步步演示如何用编译使用动态库及如何解决问题。
  当然如何还会具体的演示调用技巧。
       1.直接用编译方式使用动态库。
        动态地将程序和动态库链接,并让其在执行时加载库(如果它已在内存中则不会重复加载)
       2.直接在代码中加载动态库。
        程序在需要时再去加载一个特定的库(已加载则不必),然后调用该库中的某一特定函数.

/*
例子目录结构如下,具体代码附在页面最后。
[root@ol64 test4]# ls *
main.c

lib:
add.c calc.h sub.c

操作系统: Oracle Linux 6.4
编译版本:
[root@ol64 test4]# gcc --version
gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)
*/

编译动态库:

直接用gcc命令行搞定:


[root@ol64 lib]# gcc -o libcalc.so -m64 -fPIC -shared add.c sub.c
[root@ol64 lib]# ldd libcalc.so
linux-vdso.so.1 => (0x00007fff811ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007fc3c51e9000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e66600000)
[root@ol64 lib]# size libcalc.so
text data bss dec hex filename
1183 488 16 1687 697 libcalc.so

1.直接用编译方式使用动态库:
  gcc方式:

  一步一步看我的演示过程:


//会说找不到头文件
[root@ol64 test4]# gcc -m64 -o demo2 main.c
main.c:3:18: error: calc.h: No such file or directory

//加上-I参数后可以找到头文件了,但会提示说找到被调用的两个函数 "sub"和"add"
[root@ol64 test4]# gcc -m64 -I./lib -o demo2 main.c
/tmp/ccWoakff.o: In function `main':
main.c:(.text+0x14): undefined reference to `sub'
main.c:(.text+0x25): undefined reference to `add'
collect2: ld returned 1 exit status

//把动态库参数加上,这次顺序编译成功了。但用ldd 查看,会发现 "libcalc.so => not found"
[root@ol64 test4]# gcc -m64 -L./lib -lcalc -I./lib -o demo2 main.c
[root@ol64 test4]# ldd demo2
linux-vdso.so.1 => (0x00007fff1612e000)
libcalc.so => not found
libc.so.6 => /lib64/libc.so.6 (0x0000003e66e00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e66600000)

参数说明请看:

​​编译小结(1) GCC多平台安装(Linux,Aix,HP-UX,Solaris)及编译参数​


//不出所料,编译时会报找不到libcalc.so库文件
[root@ol64 test4]# ./demo2
./demo2: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

解决方法有好多种,


  1. 在 LD_LIBRARY_PATH 中加入路径


 

export LD_LIBRARY_PATH=`pwd`/lib


  2. /etc/ld.so.conf中用ldconfig载入


       

2.1 echo "/xcl/test4/lib" >> /etc/ld.so.conf


       

2.2 ldconfig


       

2.3 ldconfig -v|grep libcalc.so 


  3. 建个软链接到.lib或/usr/lib中


  具体为什么可以查看后面附的动态库搜索路径顺序.



在编译时的解决方法:


关键在 " -Wl,-rpath" 参数。它可以同时指定多个路径,中间用","分隔


[root@ol64 test4]# gcc -m64 -L./lib -lcalc -I./lib -Wl,-rpath,lib/ -o demo3 main.c
[root@ol64 test4]# ldd demo3
linux-vdso.so.1 => (0x00007fff7a6e5000)
libcalc.so => lib/libcalc.so (0x00007f5f0f6db000)
libc.so.6 => /lib64/libc.so.6 (0x0000003e66e00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003e66600000)
[root@ol64 test4]# ./demo3
add() = 8
sub() = 2
MAIL:xcl_168@aliyun.com

    问题: 为什么加上这个参数就可以,我不是在编译中加上了"-L"参数吗?


   

参考:http://bbs.chinaunix.net/thread-1786166-1-1.html


   

"-L" 表示在编译时查找。


   

"-Wl,-rpath" 表示在程序运行时,优先查找的顺序。



Makefile方式:

请查看:

​​ 编译小结(5) Makefile实用小结​


2.采用动态调用方式使用动态库.

代码 :



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

//#include "lib/calc.h"
#include "calc.h"
int main(void)
{
//RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;
void *handle = dlopen("/xcl/test4/lib/libcalc.so",RTLD_LAZY);
if(!handle)
{
printf("[main] open failed! %s\n",dlerror());
return -1;
}

typedef int (*sofun_add)(int x,int y);

sofun_add cadd = (sofun_add)dlsym(handle,"add");
if(!cadd)
{
printf("[main] cadd() failed! %s\n",dlerror());
dlclose(handle);
return -2;
}

printf("[main] cadd() = %d\n",cadd(3,5));
dlclose(handle);
return 0;
}


[root@ol64 test4]# gcc -m64 -I./lib -ldl -o calldemo2 callso.c
[root@ol64 test4]# ./calldemo2
[main] cadd() = 8


   如在编译时出现"undefined reference to `dlopen' "错误,说明你编译时没有加上"-ldl",即没指定dlopen所引用的库libdl.a库.


小技巧:

出现这类错误时,可能通过 man 函数名 可查到要链接的库。


如 man dlopen 时出现下面一行,告知你编译时要加上 "-ldl"


Link with -ldl.


再附一些常用的:


     lc 是link libc


    lm 是link libm


     lz 是link libz




 动态调用相关的函数说明:


  在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。


   dlopen函数常用模式:


  RTLD_LAZY 暂缓决定,表示暂时不去处理未定义函数,


     先把库装载到内存,等用到没定义的函数再说.


  RTLD_NOW 立即决定,表示马上检查是否存在未定义的函数,


     若存在,则dlopen以失败告终.


 dlsym()的第一个参数为dlopen()返回的句柄,即符号在库中的地址。


 使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。


 dlclose()用于关闭指定句柄的动态链接库,不过只有当此动态链接库的使用计数为0时,


 才会真正被系统卸载。


  上面这些函数都归属于头文件:#include <dlfcn.h>.


 


附录 :

动态链接,执行时搜索路径顺序:


1. 编译目标代码时指定的动态库搜索路径


2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径


3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径


4. 默认的动态库搜索路径/lib


5. 默认的动态库搜索路径/usr/lib



相关环境变量:


LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径


LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径



演示代码:


[root@ol64 lib]# cat calc.h
#ifndef __CALC_H_
#define __CALC_H_

int add(int x,int y);
int sub(int x,int y);

[root@ol64 lib]# cat add.c
#include "calc.h"

int add(int x,int y)
{
return (x + y);
}

[root@ol64 lib]# cat sub.c
#include "calc.h"

int sub(int x,int y)
{
return (x - y);
}


[root@ol64 test4]# cat main.c
#include <stdio.h>

#include "calc.h"

int main(void)
{
printf("add() = %d \nsub() = %d\n",add(5,3),sub(5,3));
printf("MAIL:xcl_168@aliyun.com \nBLOGn");
return 0;
}




举报

相关推荐

0 条评论