linux下创建函数库

来源:互联网 发布:java经典代码大全 编辑:程序博客网 时间:2024/06/05 21:56

说明

下文是基于linux平台来讲解静态库和动态库,并且使用C/C++编写代码。


基础知识

什么是静态库?
通俗来说以.a结尾的文件。静态库在链接时会被加入到目标代码中(直接将机器码加入到目标代码中),导致可执行文件变大,并且静态库是固定的,如果功能发生改变,静态库就不得不每次重新编译。

什么是动态库?
有.so的文件,动态库在可执行程序运行时才会被加载。动态库是可以分版本的,可执行文件引用动态库,动态库可以不断的更新。通常情况下,使用动态库会比静态库好。


静态库和动态库的比较

这里写图片描述
上图中,当使用静态库的情况下,链接时会将静态库的机器码直接加入到可执行文件中。
当使用动态库的情况下,在链接时,一个指向动态库的指针会被包含进可执行文件,但是内容并没有包含进去(和静态库不同),当运行可执行文件时,需要的动态库函数会被加载进内存里面,然后开始被调用。


创建和使用静态库

// hello.h #ifndef HELLO_H#define HELLO_H void hello();#endif /* HELLO_H */
// hello.cpp#include “hello.h”#include <iostream>void hello() {    std::cout << “Hello world” << std::endl;}
// main.cpp#include “hello.h”int main(){    hello();    return 0;}

准备好上面三个文件,接下来开始创建静态库文件并且使用动态库文件。

$ gcc -c hello.cpp                 # 产生hello.o目标文件$ ar cr libhello.a hello.o         # 创建libhello.a静态库并建立索引$ g++ -o main main.cpp -L. -lhello # 使用静态库,产生可执行文件$ ./main                           # 运行可执行文件

说明:静态库的命名规范是以lib为前缀,接着跟静态库名,拓展名为.a。
在ar cr libhello.a hello.o中,创建名为hello的静态库。


创建和使用动态库

还是上面的例子,hello.h,hello.cpp和main.cpp三个文件,创建出动态库,并且使用动态库。在完成动态库例子之前,最好先完成静态库的例子。
动态库的命名方式一般如下:
lib(前缀) + 库名 + .so(后缀) + [.版本],如:libspeak.so.1.0.8

$ g++ -fPIC -c hello.cpp                   # 编译出目标文件$ g++ -shared -fPIC -o libhello.so hello.o # 产生动态库文件$ g++ -o main mian.cpp -L. -lhello         # 使用动态库文件,产生可执行文件$ ./main                                   # 运行可执行文件,这里会产生错误

在执行./main文件时,会出现以下错误:

./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

首先解决方法很简单,将libhello.so文件拷贝一份到/usr/lib目录下,main就可以运行了。为什么会是这样,因为动态库是在运行时动态加载的,如果放到当前文件夹下,系统肯定找不到libhello.so动态库文件,而放到/usr/lib下可执行文件就能运行。

注意:上面在编译源文件带上了-fPIC选项,如果不带该选项会报错。错误信息如下:
/usr/bin/ld: hello.o: relocation R_X86_64_32 against `.rodata’ can not be used when making a shared object; recompile with -fPIC
hello.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status。

至于PIC选项的详细信息,可以看补充知识。

说明:g++ -Ldir选项,我们这里使用-L.,后面“.”表示当前目录。-llib表示连接器要链接的库名。


补充知识

刚刚遇到了可执行文件不能运行,然后将动态库移到/lib或者/usr/lib目录下就可以运行。这里不得不提一下链接器是如何找动态库?

动态库的查找路径

  1. 首先查看.dynamic段是否包含一个DT_RPATH的项。(不做过多的介绍)
  2. 查看系统是否有LD_LIBRARY_PATH的环境变量,这个变量一般用来测试新的库。
  3. 连接器查看高速缓存文件/etc/ld.so.conf,它包含了库名和路径名的对应列表。
  4. 如果上述查找失败,连接器就查找默认路径/lib,/usr/lib,如果库文件依旧没有找到,那么会提示一个错误,并且返回。(默认路径的先后顺序没有研究)

PIC选项

“PIC”选项告诉gcc产生的代码不要包含对函数和变量具体内存位置的引用,这是因为现在还无法知道使用该消息代码的应用程序会将它链接到哪一段内存地址空间。这样编译出的hello.o可以被用于建立共享链接库。建立共享链接库只需要用GCC的”-shared”标记即可。


FAQ

Q: /usr/bin/ld: hello.o: relocation R_X86_64_32 against `.rodata’ can not be used when making a shared object; recompile with -fPIC
hello.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status

A: 在编译源文件时带上-fPIC选项。
$ gcc -fPIC -c hello.cpp

Q: ./main: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

A: 将所需动态库文件放到/usr/lib目录下。


参考

[1] 什么时候使用动态和静态库:
https://stackoverflow.com/questions/140061/when-to-use-dynamic-vs-static-libraries
[2] 动态库和静态库的比较:
https://stackoverflow.com/questions/311882/what-do-statically-linked-and-dynamically-linked-mean/311889#311889
[3] relocation R_X86_64_32 against .rodata can not
http://blog.csdn.net/usbdrivers/article/details/8854256
[4] 链接器查找动态库过程:
http://blog.csdn.net/yazhouren/article/details/7708543
[5] PIC 选项:
http://blog.csdn.net/xiliang_pan/article/details/50903781


原创粉丝点击