目录

  • 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库文件。

解决办法有几个:

  1. libmyfunc.so放到gcc默认搜索目录,比如/usr/lib/x86_64-linux-gnu或者/lib/x86_64-linux-gnu都可以,这样做简单粗暴。但要是这样做的话,需要root权限来完成,除此之外,我感觉污染了整个系统。
  2. /etc/ld.so.conf.d目录下新建一个.conf文件,比如mylib.conf,在里面添加第三方库(libmyfunc.so)目录路径
  3. 设置LD_LIBRARY_PATH环境变量,比如export LD_LIBRARY_PATH=.。设置这个环境变量之后,操作系统将在先在LD_LIBRARY_PATH下搜索库文件,再到默认路径中搜索文件。这样,可执行文件就可以在LD_LIBRARY_PATH中找到第三方库(libmyfunc.so)的位置。
  4. 编译的时候添加-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. 参考资料

  1. Linux 动态库剖析
  2. Linux动态链接