linux静态库与共享库(二)

刺骨的言语ヽ痛彻心扉 2022-09-18 07:53 215阅读 0赞

原文地址:

http://blog.csdn.net/hmalloc/article/details/8463325

6 库的使用

下面来写个程序测试一下前面两个库文件,代码如下:

[cpp] view plain copy print ?

  1. /* say.c */
  2. #include
  3. #include
  4. void say_something(const char *str);
  5. int main(int argc, char **argv)
  6. {
  7. say_something(argv[1]);
  8. exit(0);
  9. }

下图中显示了生成的两个库文件:

1356839875_8454.PNG

静态库的使用

执行如下命令编译链接:

[plain] view plain copy print ?

  1. gcc –c say.c –o say.o
  2. gcc –o say say.o libsay.a

这将生成可执行文件say,./say加参数即可运行。这就是静态库,它只在链接时需要。

静态库使用很简单,但有一点需要注意,就是——链接顺序。如果你按如下顺序链接:

[plain] view plain copy print ?

  1. gcc -o say libsay.a say.o

将得到这样的错误提示:

[plain] view plain copy print ?

  1. say.o: In function `main’:
  2. say.c:(.text+0x15): undefined reference to `say_something’
  3. collect2: ld returned 1 exit status

这是初学者经常碰到的问题,尤其当项目比较庞大库文件较多时。因为链接器(ld)按输入库顺序链接,libsay.a先输入,然后它发现say_something并未被使用,所以并不会链接,所以会报这样的错误。库文件的链接顺序尤为重要,其基本原则就是越独立越底层的库应尽量靠后放。

共享库的使用

共享库的使用有多种方法,我总结了五种,下面一一道来。

我们生成的共享库文件为 libsay.so.1.0.0,为了链接方便,我们用如下命令创建两个符号链接:

[plain] view plain copy print ?

  1. ln -s libsay.so.1.0.0 libsay.so.1
  2. ln -s libsay.so.1 libsay.so

创建后如图所示:

1356842548_8942.PNG

方法一:

[plain] view plain copy print ?

  1. gcc say.c –o sayso –L. –Wl,-rpath=. –lsay

方法一用-L指定链接时搜索路径而用-rpath指定运行时搜索路径,编译后生成可执行文件sayso,可直接运行。

为了验证生成的sayso文件的依赖性,可用ldd(查看可执行文件的共享库依赖性)命令查看一下,如图所示:

1356842867_3531.PNG

我们还可以用 readelf 命令查看 rpath 即运行时路径:

1356843067_1146.PNG

在接下来的几种方法中我们同样可以用这两个命令查看依赖性。

方法二:

[plain] view plain copy print ?

  1. export LD_LIBRARY_PATH=`pwd`
  2. gcc –L. say.c –o sayso2 –lsay

方法二首先设置环境变量LD_LIBRARY_PATH为当前目录(小技巧,用`pwd`代替),因为共享库文件在当前目录下,这样运行可执行文件后也能找到该库文件。

方法三:

将共享库文件libsay.so.1.0.0复制到/usr/lib下,并创建 libsay.so libsay.so.1等符号链接(创建符号链接前面有提到),然后运行如下命令:

[plain] view plain copy print ?

  1. gcc say.c –o sayso3 –lsay

从以上命令看到少了–L选项,因为/usr/lib是系统默认搜索路径,所以不必指定,但LD_LIBRARY_PATH对-l无效,所以方法二中依然需要–L选项。

为了验证方法三是可行的,我们可以把LD_LIBRARY_PATH重新置为空:

[plain] view plain copy print ?

  1. export LD_LIBRARY_PATH=

然后再运行./sayso3发现运行正常,下图中显示了这种差异,由此也看出LD_LIBRARY_PATH的优先级别高于/usr/lib。

1356844150_7875.PNG

将/usr/lib中刚刚copy的文件删除,来试验一下方法四,这个时候运行./sayso3应该提示找不到库文件。

方法四:

接下来我们要用到一个文件——/etc/ld.so.conf(笔者的系统是ubuntu 10.04),这个文件记录了一些共享库搜索路径(或者完整的共享库名),查看其内容不难发现其用法,我们可以将我们的共享库所在路径添加到这里来,要使添加的路径有效,我们还必须以root权限执行ldconfig命令,该命令会根据ld.so.conf文件,在/etc目录下生成ld.so.cache文件,ld.so.cache文件才是系统运行时搜索库的关键。

cat ld.so.conf文件发现其内容如下:

include /etc/ld.so.conf.d/*.conf

所以我们只要在/etc/ld.so.conf.d目录下创建相应的conf文件即可。cd到该目录,用root权限创建 libsay.conf 文件,将共享库文件所在路径添加到该文件中保存,执行ldconfig。然后我们运行./sayso3,发现又能运行了。

方法五:

接下来是终极方法,也是非常有用有意思的方法!

方法五要用到系统库dl(专门服务于共享库加载),该库提供了以下几个函数:

dlopen: 将共享库载入内存,返回一个句柄
dlsym: 返回函数地址
dlclose: 从共享库中释放当前程序
dlerror: 返回出错字符串,如果没有则为空
dl库用法示例:

[cpp] view plain copy print ?

  1. /* saydl.c */
  2. #include
  3. #include
  4. #include
  5. int main(int argc, char **argv)
  6. {
  7. void (*say)(const char *);
  8. char *error;
  9. void *handle;
  10. handle = dlopen(“libsay.so”, RTLD_LAZY);
  11. if (error = dlerror()) {
  12. printf(“%s\n”, error);
  13. exit(1);
  14. }
  15. say = dlsym(handle, “say_something”);
  16. if (error = dlerror()) {
  17. printf(“%s\n”, error);
  18. exit(1);
  19. }
  20. say(“hello, world!\n”);
  21. dlclose(handle);
  22. exit(0);
  23. }

编译这段代码:

[plain] view plain copy print ?

  1. gcc saydl.c -o saydl -ldl

可以看到,在这里根本不需要指定 libsay.so 库相关路径,因为程序运行时通过dlopen动态加。虽然如此,程序运行还是会按照运行时库搜索优先级搜索的,除非dlopen采用了绝对路径。看到这个方法相信你会很惊讶,不要惊讶,请深入体会,这个方法很有价值。

7 总结

好了,如果你能从头到尾将这篇文章看完,并亲自动手实践,我相信你对于库的使用及相关问题应该游刃有余了,也说明你有足够的求知欲。从这里我们也可以看出linux高深的技术,这同时也是一种艺术,大神们为linux所做的工作令人叹服!

我查阅了大量资料以撰写该文章,请相信我这篇文章对你有用!这些看似烦琐细腻的技术,已给我带来极大的乐趣与帮助!

发表评论

表情:
评论列表 (有 0 条评论,215人围观)

还没有评论,来说两句吧...

相关阅读