目录
- 1. 编写C程序
- 2. 编译动态链接库
- 3. 使用共享库
- 4. 执行程序
- 5. 参考资料
1. 编写C程序
- 比如编写
myfunc.c
文件,里面包含两个函数,一个是say_hello
,另一个是cal_sum
。
#include "myfunc.h"
void say_hello()
{
printf("hello world\n");
}
int cal_sum(int x, int y)
{
return x + y;
}
- 为
myfunc.c
编写接口文件
#ifndef __MY_FUNC_H
#define __MY_FUNC_H
#include <stdio.h>
#include <stdlib.h>
void say_hello();
int cal_sum(int x, int y);
#endif
2. 编译动态链接库
- 首先编译
myfunc.c
:
gcc -c -fPIC -o myfunc.o myfunc.c
-c
表示只编译(compile),而不链接,输出目标文(obj文件)。-o
表示输出文件的文件名。-fPIC
PIC指Position Independent Code, 生成适合在共享库中使用的与位置无关的代码。编译成共享库要求此选项。适用于动态链接并避免对全局偏移表大小的任何限制。
- 生成共享库文件
libmyfunc.so
gcc -shared myfunc.o -o libmyfunc.so
-share
生成一个共享对象,然后可以与其他对象链接以形成可执行文件。
两条命令合成一条就是:
gcc -fPIC -shared myfunc.c -o libmyfunc.so
3. 使用共享库
接下来我们使用test.c
来调用共享库。test.c
内容如下:
#include "myfunc.h"
int main(int argc, char const *argv[])
{
int result = 0;
say_hello();
result = cal_sum(2, 3);
printf("%d\n", result);
return 0;
}
编译上述包含.h
头文件的程序,GCC编译器需要知道头文件的位置
- 对于
#include <...>
,GCC编译器会在默认include搜索路径中寻找。 - 对于
#include "..."
,GCC编译器会在当前路径搜索.h
文件。你也可以使用-I
选项提供额外的搜索路径,比如-I /home/test/
。
除此之外,GCC编译器还需要知道我们用了哪个库文件,库文件在哪里
- 使用
-l
选项说明库文件的名字。这里,我们使用的是libmyfunc.so
库文件,所以选项是这样写的:-l myfunc
- 使用
-L
选项说明库文件的路径。这里,我们的库文件是在当前路径,所以选项是这样写的:-L .
(.
表示当前路径)。
所以,最终我们链接库文件生成的可执行文件命令是这样的:
gcc -o test test.c -l myfunc -L .
附加:
可以使用下面的命令,来获知系统的include默认搜索路径:
$ gcc -print-prog-name=cc1 -v
获知库默认搜索路径:
$ gcc -print-search-dirs
4. 执行程序
$ ./test
执行程序后发现出现这样的情况:./test: error while loading shared libraries: libmyfunc.so: cannot open shared object file: No such file or directory
这是因为执行程序的时候,操作系统不知道libmyfunc.so
的位置,系统无法找到libmyfunc.so
库文件。尽管我们用GCC编译的时候,用-L
选项提供了libmyfunc.so
文件的位置,但是这个信息并没有被写入到可执行程序里面。下面用命令ldd
命令测试(ldd
命令是用于显示可执行文件所依赖的库):
$ ldd test
linux-vdso.so.1 => (0x00007ffccc9fe000)
libmyfunc.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0d31a44000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0d31e0e000)
可以看出可执行文件test
无法找到libmyfunc.so
库文件。
解决办法有几个:
- 将
libmyfunc.so
放到gcc默认搜索目录,比如/usr/lib/x86_64-linux-gnu
或者/lib/x86_64-linux-gnu
都可以,这样做简单粗暴。但要是这样做的话,需要root权限来完成,除此之外,我感觉污染了整个系统。 - 在
/etc/ld.so.conf.d
目录下新建一个.conf
文件,比如mylib.conf
,在里面添加第三方库(libmyfunc.so
)目录路径 - 设置
LD_LIBRARY_PATH
环境变量,比如export LD_LIBRARY_PATH=.
。设置这个环境变量之后,操作系统将在先在LD_LIBRARY_PATH
下搜索库文件,再到默认路径中搜索文件。这样,可执行文件就可以在LD_LIBRARY_PATH
中找到第三方库(libmyfunc.so
)的位置。 - 编译的时候添加
-Wl,-rpath
选项,比如gcc -o test test.c -l myfunc -L . -Wl,-rpath=.
。-Wl
选项告诉编译器将后面的参数传递给链接器
最后运行可执行文件test
的结果:
$ gcc -o test test.c -l myfunc -L . -Wl,-rpath=.
$ ./test
hello world
5
5. 参考资料