Linux下编译链接动态库

记录下Linux下编译和链接动态库的过程。

一、 编写动态库

头文件so.h:

#ifndef  SO_H
#define  SO_H

int add(int a, int b);


#endif  /*SO_H*/

实现文件so.c:

#include "so.h"

int add(int a, int b)
{
    return a + b;
}

二、编译动态库

gcc so.c -fPIC -shared -o libtest.so

解释一下各个参数含义:

  1. -fPIC:生成位置无关代码(Position Independent Code),只有生成PIC才能通过虚拟页映射达到可执行代码在进程间共享,从而节省内存的目的。
  2. 说明我们要生成的是动态库so(Shared Object)文件,从而进行动态链接。

通过nm -g libtest.so可以看到,导出符号表中已经有add这个符号了:

$ nm -g libtest.so 
0000000000000670 T add
0000000000201030 B __bss_start
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000201030 D _edata
0000000000201038 B _end
0000000000000684 T _fini
                 w __gmon_start__
0000000000000540 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses

三、使用动态库

1. 隐式链接(编译时链接)

编写主程序main.c:

#include <stdio.h>
#include "so.h"

int main(int argc, char *argv[])
{
    printf("%d\n", add(1, 2));
    return 0;
}

使用gcc main.c -L. -ltest -o test进行编译。

  • -L:添加库文件的搜索路径
  • -l:指定需要链接的库。该名称是处在头lib和后缀.so中的名称,如上动态库libtest.so的l参数为-l test

此时通过readelf test -d已经能看到生成的可执行文件test的Dynamic section里依赖libtest.so了

$ readelf test -d

Dynamic section at offset 0xe18 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libtest.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
......

dynamic symbols中也有一个undefined symbol(add)

$ nm -D test 
                 U add
0000000000601048 B __bss_start
0000000000601048 D _edata
0000000000601050 B _end
00000000004007b4 T _fini
                 w __gmon_start__
0000000000400578 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U __libc_start_main
                 U printf

在执行隐式链接的程序之前要注意设置LD_LIBRARY_PATH环境变量,或者把前面生成的libtest.so复制到系统路径下,否则会找不到动态库。

$ ./test 
./test: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory

$ export LD_LIBRARY_PATH=.

$ ./test 
3

2. 显式链接(运行时链接)

编写主程序dyn_main.c

#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char *argv[])
{
    void *dl = NULL;
    int (*add)(int a, int b);
    dl = dlopen( "./libtest.so", RTLD_LAZY);
    if( dl == NULL )
    {
        printf("so loading error.\n");
        return 1;
    }
    add = (int(*)(int, int))dlsym(dl, "add");
    if( dlerror() != NULL )
    {
        printf("fun load error.\n");
        return 1;
    }
    printf("%d\n", add(1, 2));
    return 0;
}

使用gcc dyn_main.c -ldl -o dyn_test编译。

这时通过readelf dyn_test -d可以发现,dyn_test不依赖libtest.so:

$ readelf dyn_test -d

Dynamic section at offset 0xe18 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
......

dyn_test的dynamic symbols中也没有add:

$ nm -D dyn_test 
                 U dlerror
                 U dlopen
                 U dlsym
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
                 U __libc_start_main
                 U printf
                 U puts

运行程序也不需要设置LD_LIBRARY_PATH环境变量

$ ./dyn_test 
3

参考资料:

  1. Linux动态库的编译与使用 转载
  2. 详细分析Linux动态库的使用方式
  3. How do I list the symbols in a .so file
  4. Import names in ELF binary

Comments

Comments powered by Disqus