linux下封装函数库——动态库.so和静态库.a(代码实现及链接方式)

在linux环境下的链接库分为静态链接库(.a库)和动态链接库(.so库),其作用是把C程序编译好做成一种可执行链接文件,主程序文件调用这些程序的函数接口是可以使用a库或so库,在主程序中只需要include含有库中提供的函数接口声明的头文件即可。所以学会如何将自己的源代码封装成库进行使用很重要,接下来就来介绍一下然后进行库的封装:
1、动态链接库的制作:
首先,生成一个简单的函数实现mymax(a,b),将它封装成库来进行举例:
代码如下:

  1 int mymax(int a, int b)
  2 {
  3     if(a > b)
  4         return a;
  5     return b;
  6 }
                

这里可以通过gcc命令才生成.so库,也可以编写Makefile生成so库,Makefile如下:

  1 CC = gcc
  2 SRCS = mymax.c
  3 OBJS = $(SRCS:.c=.o)
  4 EXEC = libmymax.so//这里要注意生成的库文件开头必须lib开头,后缀.so
  5 start:$(OBJS)
  6         $(CC) -o $(EXEC) $(OBJS) -shared//这表明是一个动态库
  7 .c.o:
  8         $(CC) -o $@ -c $< -fPIC//表示编译函数时没有偏移地址
  9 clean:
 10         rm -rf $(OBJS)

当然也可以直接使用指令:
linux下封装函数库——动态库.so和静态库.a(代码实现及链接方式)_第1张图片

这里一定要注意so库的前缀必须为lib,然后将.so库链接到主程序main.c中,这里需要注意一下,因为动态库的特性,编译器会到指定的目录去寻找动态库,目录的地址在/etc/ld.so.conf.d/ 目录里的libc.conf文件里,你可以在里面加一行地址表示你so库的位置,更改完conf文件里的内容,记得输入命令行:ldconfig。
你还可以将so库复制到默认的目录下。这里是将so库复制到了默认目录下,生成可执行文件并运行,最后进行编译链接使用Makefile如下:

  1 CC = gcc
  2 SRCS = mylib.c
  3 OBJS = $(SRCS:.c=.o)
  4 EXEC = myapp
  5 start:$(OBJS)
  6         $(CC) -o $(EXEC) $(OBJS) -L. -lmymax
  7 .c.o:
  8         $(CC) -o $@ -c $< 
  9 clean:
 10         rm -rf $(OBJS)

这样就生成了可执行程序myapp,
注意:在加载动态链接库的时候,有可能会遇到加载不到的错误,原因在于系
统默认加载的动态链接库路径里没有找到你的动态库,有三种解决方法:
**1.在执行gcc main.c -L. -ltest -o main 前,执行 export **
LD_LIBRARY_PATH=$(pwd)
2.将你so所在的目录写到/etc/ld.so.conf文件里,然后执行ldconfig。
3.将你的so放在/etc/ld.so.conf里的路径位置里。

最后可以通过指令ldd查看执行程序的链接库都有哪些:
linux下封装函数库——动态库.so和静态库.a(代码实现及链接方式)_第2张图片

静态链接库和动态链接库的区别在于,主程序在运行前,静态链接库的链接固定写入在程序中,而动态链接库则是在每次程序运行再加载链接。
接下来制作静态链接库:
linux下封装函数库——动态库.so和静态库.a(代码实现及链接方式)_第3张图片
最后使用nm命令可以查看程序依赖的库信息:
linux下封装函数库——动态库.so和静态库.a(代码实现及链接方式)_第4张图片
从中可以找到我们进行链接的静态库信息。

最后,如果对其他一些细节还需要详细了解的话,推荐一个博客,我觉得写得真的很相信,http://www.cnblogs.com/skynet/p/3372855.html

加载动态库需要指定路径的问题

gcc中的rpath参数可以用编译时指定动态库的搜索路径,这样运行时就不需要export LD_LIBRARY_PATH了。
解决方案

  • 方案一

编译时增加参数-Wl,-rpath=’.’

可执行程序运行时会搜索当前工作目录(不是进程所在目录),所以如果在其它目录运行该可执行程序时会提示找不到动态库(同样,如果建立一个软连接ln -s时,也会提示找不到动态库)。

  • 方案二

编译时增加参数-Wl,-z,origin -Wl,-rpath=’$ORIGIN’

O R I G I N 表 示 会 搜 索 进 程 所 在 目 录 ( 同 样 也 可 以 设 置 − r p a t h = ’ ORIGIN表示会搜索进程所在目录(同样也可以设置-rpath=’ ORIGIN(rpath=ORIGIN/lib’)。此方案不会出现方案一中软链接找不到动态库的情况。

  • 补充

通常情况下使用第二种方案是比较理想的,但是为了防止提升权限的漏洞,一旦进行了提升权限操作(比如chown root ping ;chmod u+s ping),则ORIGIN的设置会失效,运行可执行程序,会提示找不到动态库,即使使用export LD_LIBRARY_PATH设置了路径也无效。

要解决此问题:

  • 使用绝对路径(此方案不可取,所以没有亲自试验,应该是-rpath时指定绝对路径)
  • 将所依赖的so文件拷贝到操作系统默认会搜索的目录下,比如/lib或者/lib64等目录
    查看编译好的动态库或者进程的rpath的方法
linux
readelf -d xxx.lib
solaris
greadelf -d xxx.lib

动态库的搜索路径的顺序

编译目标代码时指定的动态库搜索路径;
环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径;
配置文件 /etc/ld.so.conf 中指定的动态库搜索路径;
默认的动态库搜索路径 /lib ;
默认的动态库搜索路径 /usr/lib

大家共同进步
linux下封装函数库——动态库.so和静态库.a(代码实现及链接方式)_第5张图片

你可能感兴趣的:(嵌入式相关基础知识,C语言基础)