精华内容
下载资源
问答
  • Linux程序编译链接动态库版本的问题

    千次阅读 2015-08-17 15:40:01
    在实际生产环境中,程序的编译和运行往往是分开的,但只要搞清楚这一系列过程中的原理,就不怕被动态库的版本搞晕。简单来说,按如下方式来做 编译动态库时指定-Wl,-soname,libxxx.so.a,设置soname为libxxx.so.a,...

    不同版本的动态库可能会不兼容,如果程序在编译时指定动态库是某个低版本,运行是用的一个高版本,可能会导致无法运行。Linux上对动态库的命名采用libxxx.so.a.b.c的格式,其中a代表大版本号,b代表小版本号,c代表更小的版本号,我们以Linux自带的cp程序为例,通过ldd查看其依赖的动态库

     $ ldd /bin/cp                                                                                                                                                                                        
    linux-vdso.so.1 =>  (0x00007ffff59df000)
    libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fb3357e0000)
    librt.so.1 => /lib64/librt.so.1 (0x00007fb3355d7000)
    libacl.so.1 => /lib64/libacl.so.1 (0x00007fb3353cf000)
    libattr.so.1 => /lib64/libattr.so.1 (0x00007fb3351ca000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fb334e35000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007fb334c31000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fb335a0d000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb334a14000)
    

    左边是依赖的动态库名字,右边是链接指向的文件,再查看libacl.so相关的动态库

      $ ll /lib64/libacl.so*                                                                                                                                                                               
    lrwxrwxrwx. 1 root root    15 1月   7 2015 /lib64/libacl.so.1 -> libacl.so.1.1.0
    -rwxr-xr-x. 1 root root 31280 12月  8 2011 /lib64/libacl.so.1.1.0
    

    我们发现libacl.so.1实际上是一个软链接,它指向的文件是libacl.so.1.1.0,命名方式符合我们上面的描述。也有不按这种方式命名的,比如

    $ ll /lib64/libc.so*                                                                                                                                                                                  
    lrwxrwxrwx 1 root root 12 8月  12 14:18 /lib64/libc.so.6 -> libc-2.12.so
    

    不管怎样命名,只要按照规定的方式来生成和使用动态库,就不会有问题。而且我们往往是在机器A上编译程序,在机器B上运行程序,编译和运行的环境其实是有略微不同的。下面就说说动态库在生成和使用过程中的一些问题

    动态库的编译

    我们以一个简单的程序作为例子

    // filename:hello.c
    #include <stdio.h>
    
    void hello(const char* name)
    {
        printf("hello %s!\n", name);
    }
    
    // filename:hello.h
    void hello(const char* name);
    

    采用如下命令进行编译

    gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.1
    

    需要注意的参数是-Wl,soname(中间没有空格),-Wl选项告诉编译器将后面的参数传递给链接器,
    -soname则指定了动态库的soname(简单共享名,Short for shared object name)

    现在我们生成了libhello.so.0.0.1,当我们运行ldconfig -n .命令时,当前目录会多一个软连接

     $ ll libhello.so.0                                                                                                                                                                                   
    lrwxrwxrwx 1 handy handy 17 8月  17 14:18 libhello.so.0 -> libhello.so.0.0.1
    

    这个软链接是如何生成的呢,并不是截取libhello.so.0.0.1名字的前面部分,而是根据libhello.so.0.0.1编译时指定的-soname生成的。也就是说我们在编译动态库时通过-soname指定的名字,已经记载到了动态库的二进制数据里面。不管程序是否按libxxx.so.a.b.c格式命名,但Linux上几乎所有动态库在编译时都指定了-soname,我们可以通过readelf工具查看soname,比如文章开头列举的两个动态库

     $ readelf -d /lib64/libacl.so.1.1.0                                                                                                                                                                   
    
    Dynamic section at offset 0x6de8 contains 24 entries:
    Tag        Type                         Name/Value
    0x0000000000000001 (NEEDED)             Shared library: [libattr.so.1]
    0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
    0x000000000000000e (SONAME)             Library soname: [libacl.so.1]
    

    这里省略了一部分,可以看到最后一行SONAME为libacl.so.1,所以/lib64才会有一个这样的软连接

    再看libc-2.12.so文件,该文件并没有采用我们说的命名方式

     $ readelf -d /lib64/libc-2.12.so                                                                                                                                                                      
    
    Dynamic section at offset 0x18db40 contains 27 entries:
    Tag        Type                         Name/Value
    0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
    0x000000000000000e (SONAME)             Library soname: [libc.so.6]
    

    同样可以看到最后一行SONAME为libc.so.6,即便该动态库没有按版本号的方式命名,但仍旧有一个软链指向该动态库,而该软链的名字就是soname指定的名字

    所以关键就是这个soname,它相当于一个中间者,当我们的动态库只是升级一个小版本时,我们可以让它的soname相同,而可执行程序只认soname指定的动态库,这样依赖这个动态库的可执行程序不需重新编译就能使用新版动态库的特性

    可执行程序的编译

    还是以hello动态库为例,我们写一个简单的程序

    // filename:main.c
    #include "hello.h"
    
    int main()
    {
        hello("handy");
        return 0;
    }
    

    现在目录下是如下结构

    ├── hello.c
    ├── hello.h
    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    └── main.c
    

    libhello.so.0.0.1是我们编译生成的动态库,libhello.so.0是通过ldconfig生成的链接,采用如下命令编译main.c

     $ gcc main.c -L. -lhello -o main                                                                                                                                                                            
    /usr/bin/ld: cannot find -lhello
    

    报错找不到hello动态库,在Linux下,编译时指定-lhello,链接器会去寻找libhello.so这样的文件,当前目录下没有这个文件,所以报错。建立这样一个软链,目录结构如下

    ├── hello.c
    ├── hello.h
    ├── libhello.so -> libhello.so.0.0.1
    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    └── main.c
    

    让libhello.so链接指向实际的动态库文件libhello.so.0.0.1,再编译main程序

    gcc main.c -L. -lhello -o main
    

    这样可执行文件就生成了。通过以上测试我们发现,在编译可执行程序时,链接器会去找它依赖的libxxx.so这样的文件,因此必须保证libxxx.so的存在

    用ldd查看其依赖的动态库

     $ ldd main                                                                                                                                                                                            
            linux-vdso.so.1 =>  (0x00007fffe23f2000)
            libhello.so.0 => not found
            libc.so.6 => /lib64/libc.so.6 (0x00007fb6cd084000)
            /lib64/ld-linux-x86-64.so.2 (0x00007fb6cd427000)
    

    我们发现main程序依赖的动态库名字是libhello.so.0,既不是libhello.so也不是libhello.so.0.0.1。其实在生成main程序的过程有如下几步

    • 链接器通过编译命令-L. -lhello在当前目录查找libhello.so文件
    • 读取libhello.so链接指向的实际文件,这里是libhello.so.0.0.1
    • 读取libhello.so.0.0.1中的SONAME,这里是libhello.so.0
    • 将libhello.so.0记录到main程序的二进制数据里

    也就是说libhello.so.0是已经存储到main程序的二进制数据里的,不管这个程序在哪里,通过ldd查看它依赖的动态库都是libhello.so.0

    而为什么这里ldd查看main显示libhello.so.0为not found呢,因为ldd是从环境变量$LD_LIBRARY_PATH指定的路径里来查找文件的,我们指定环境变量再运行如下

     $ export LD_LIBRARY_PATH=. && ldd main                                                                                                                                                                
        linux-vdso.so.1 =>  (0x00007fff7bb63000)
        libhello.so.0 => ./libhello.so.0 (0x00007f2a3fd39000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f2a3f997000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2a3ff3b000)
    

    可执行程序的运行

    现在测试目录结果如下

    ├── hello.c
    ├── hello.h
    ├── libhello.so -> libhello.so.0.0.1
    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    ├── main
    └── main.c
    

    这里我们把编译环境和运行环境混在一起了,不过没关系,只要我们知道其中原理,就可以将其理清楚

    前面我们已经通过ldd查看了main程序依赖的动态库,并且指定了LD_LIBRARY_PATH变量,现在就可以直接运行了

     $ ./main                                                                                                                                                                                              
    hello Handy!
    

    看起来很顺利。那么如果我们要部署运行环境,该怎么部署呢。显然,源代码是不需要的,我们只需要动态库和可执行程序。这里新建一个运行目录,并拷贝相关文件,目录结构如下

    ├── libhello.so.0.0.1
    └── main
    

    这时运行会main会发现

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

    报错说libhello.so.0文件找不到,也就是说程序运行时需要寻找的动态库文件名其实是动态库编译时指定的SONAME,这也和我们用ldd查看的一致。通过ldconfig -n .建立链接,如下

    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    └── main
    

    再运行程序,结果就会符合预期了

    从上面的测试看出,程序在运行时并不需要知道libxxx.so,而是需要程序本身记载的该动态库的SONAME,所以main程序的运行环境只需要以上三个文件即可

    动态库版本更新

    假设动态库需要做一个小小的改动,如下

    // filename:hello.c
    #include <stdio.h>
    
    void hello(const char* name)
    {
        printf("hello %s, welcom to our world!\n", name);
    }
    

    由于改动较小,我们编译动态库时仍然指定相同的soname

    gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.2
    

    将新的动态库拷贝到运行目录,此时运行目录结构如下

    ├── libhello.so.0 -> libhello.so.0.0.1
    ├── libhello.so.0.0.1
    ├── libhello.so.0.0.2
    └── main
    

    此时目录下有两个版本的动态库,但libhello.so.0指向的是老本版,运行ldconfig -n .后我们发现,链接指向了新版本,如下

    ├── libhello.so.0 -> libhello.so.0.0.2
    ├── libhello.so.0.0.1
    ├── libhello.so.0.0.2
    └── main
    

    再运行程序

     $ ./main                                                                                                                                                                                              
    hello Handy, welcom to our world!
    

    没有重新编译就使用上了新的动态库, wonderful!

    同样,假如我们的动态库有大的改动,编译动态库时指定了新的soname,如下

    gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.1 -o libhello.so.1.0.0
    

    将动态库文件拷贝到运行目录,并执行ldconfig -n .,目录结构如下

    ├── libhello.so.0 -> libhello.so.0.0.2
    ├── libhello.so.0.0.1
    ├── libhello.so.0.0.2
    ├── libhello.so.1 -> libhello.so.1.0.0
    ├── libhello.so.1.0.0
    └── main
    

    这时候发现,生成了新的链接libhello.so.1,而main程序还是使用的libhello.so.0,所以无法使用新版动态库的功能,需要重新编译才行

    最后

    在实际生产环境中,程序的编译和运行往往是分开的,但只要搞清楚这一系列过程中的原理,就不怕被动态库的版本搞晕。简单来说,按如下方式来做

    • 编译动态库时指定-Wl,-soname,libxxx.so.a,设置soname为libxxx.so.a,生成实际的动态库文件libxxx.so.a.b.c,
    • 编译可执行程序时保证libxx.so存在,如果是软链,必须指向实际的动态库文件libxxx.so.a.b.c
    • 运行可执行文件时保证libxxx.so.a.b.c文件存在,通过ldconfig生成libxxx.so.a链接指向libxxx.so.a.b.c
    • 设置环境变量LD_LIBRARY_PATH,运行可执行程序

    EOF

    展开全文
  • 本文的目的是测试各种类型编译后的使用效果,包括链接其他编译方法,使用方法,依赖性等。 太长不看版:请跳至文章最后的总结对比表。 一。内容包含: ①静态libbb.a依赖静态libaa.a的测试; ②...

    本文的目的是测试各种类型库的编译后的使用效果,包括库又链接其他库的编译方法,使用方法,依赖性等。

    太长不看版:请跳至文章最后的总结对比表。

    一。内容包含:

    ①静态库libbb.a依赖静态库libaa.a的测试;

    ②静态库libbb.a依赖动态库libaa.so的测试;

    ③动态库libbb.so依赖静态库libaa.a的测试;

    ④动态库libbb.so依赖动态库libaa.so的测试;

    所用到的文件列表:

    aalib.cpp aalib.h        bblib.cpp bblib.h     main.cpp

    二。源代码:

    aalib.h

    #ifndef AALIB_H
    #define AALIB_H
    #include "stdio.h"
    void helloAA();

    #endif

    ********分割线**************

    aalib.cpp

    #include "aalib.h"

    void helloAA()
    {
        printf(" lib AAAAAAAAAAAAA\n ");
    }

    ********分割线**************

    bblib.h

    #ifndef BBLIB_H
    #define BBLIB_H

    void helloBB();
    void helloAB();

    #endif

    ********分割线**************

    bblib.cpp

    #include "bblib.h"
    #include "aalib.h"

    void helloBB()
    {
        printf(" lib BBBBBBBBBBBBBBBB \n");
    }

    void helloAB()
    {
        printf(" *a*******************\n");
        helloAA();
        helloBB();
        printf(" *b*******************\n");
    }

    ********分割线**************

    main.cpp

    #include "bblib.h"

    int main()
    {
        helloBB();

        helloAB();
    }
     

    三。测试过程

    ①静态库libbb.a依赖静态库libaa.a的测试

    现在生成了静态库libaa.a和libbb.a,下面测试静态库的使用方法:

    可以看到链接静态库的话要指定静态库依赖的静态库,否则编译不过去!由此可见轻易不要链接静态库,因为你也许不知道你要用的静态库还依赖着哪些库.......

    那么将所依赖的静态库编译进静态库呢?试试

    可以看到,使用命令ar rcs libbb.a bblib.o libaa.a并没有起到作用,亦即libaa.a并没有被编译进libbb.a,因此编译可执行程序时还需要链接libaa.a。

    改用下面这种方式编译,可行:

    即编译静态库libbb.a时不要链接libaa.a而是用aalib.o文件,亦即编译静态库链接静态库时不能直接链接静态库,而是用形成该静态库的那些.o文件,可以使用 ar -x libaa.a来获取静态库的所有.o文件(注意:所有.o文件的顺序要对,被依赖的要放后面)。

    ②静态库libbb.a依赖动态库libaa.so的测试

    第一行命令:gcc -o libaa.so -shared -fPIC aalib.cpp

    等价于:gcc -c -shared -fPIC aalib.cpp 加 gcc -o libaa.so -shared -fPIC aalib.o 这两条(注意编译.o时要加选项-shared -fPIC,否则无法生存.so)

    可以看到,编译可执行程序时只链接静态库不链接动态库编译不过去!运行时也需要动态库libaa.so支持

    那么将动态库编译进静态库中呢?试试

    可以看到,虽然编译静态库libbb.a的时候链接了动态库libaa.so,编译可执行程序只链接静态库libbb.a也能编译过去,但是在执行时还是要libaa.so动态库支持。

    ③动态库libbb.so依赖静态库libaa.a的测试

    可以看到编译libbb.so时链接静态库出错,提示静态库需要使用-fPIC选项来编译,那就按照提示的来修改编译命令:

    静态库所需的.o文件使用-shared -fPIC编译,然后在编译动态库的时候链接静态库好用了。并且编译可执行文件时不用链接被依赖的静态库。

    来看一下这个动态库libbb.so依赖哪些库:

    可以看到没有依赖libaa.a这个静态库。

    ④动态库libbb.so依赖动态库libaa.so的测试

    动态库链接动态库成功,并且编译可执行文件时也不用链接被依赖的库,但是运行时还是要两个动态库支持。

    四。总结

    被依赖的库aa编译库aa的命令直接库bb编译库bb的命令编译可执行程序的命令编译时是否需要库aa运行时是否需要库aa
    libaa.a

    gcc -c aalib.cpp

     ar rcs libaa.a aalib.o

    libbb.agcc -c bblib.cpp                                ar rcs libbb.a bblib.ogcc -o out main.cpp libbb.a libaa.a需要不需要
    libaa.agcc -c aalib.cpp      ar rcs libaa.a aalib.olibbb.agcc -c bblib.cpp                               ar rcs libbb.a bblib.o aalib.ogcc -o out main.cpp libbb.a 不需要不需要
    libaa.sogcc -o libaa.so -shared -fPIC aalib.cpplibbb.agcc -c bblib.cpp                               ar rcs libbb.a bblib.o libaa.sogcc -o out main.cpp libbb.a不需要需要
    libaa.agcc -c aalib.cpp      ar rcs libaa.a aalib.olibbb.sogcc -o libbb.so -shared -fPIC bblib.cpp libaa.a编译libbb.so就出错,无法编译成动态库  
    libaa.agcc -c -shared -fPIC aalib.cpp               ar rcs libaa.a aalib.olibbb.sogcc -o libbb.so -shared -fPIC bblib.cpp libaa.agcc -o out main.cpp -L./ -lbb不需要不需要
    libaa.sogcc -o libaa.so -shared -fPIC aalib.cpplibbb.sogcc -o libbb.so -shared -fPIC bblib.cpp -L./ -laagcc -o out main.cpp -L./ -lbb不需要需要

    其他说明:链接静态库时要注意顺序,使用第二行的方法编译直接库libbb.a的时候用到被依赖库的.o文件,注意些个.o文件的依赖顺序也很重要,不能错,被依赖的要放后面。另外:编译好的动态库要指定路径,否则运行时找不到动态库的位置。

    五。结论

        由于本测试的初衷是找出一种方法使得编译可执行程序时不链接那些被依赖的库,因此第二行和第五行的组合可以作为备选方案。

    展开全文
  • 指明需要链接动态库 libmytest.so 7 LIBPATH = -L./dynamiclib/ libs 指明 libmytest.so 的路径 8 #search paths for errorhandler.c 9 vpath %.c ./ comm 10 #下行是为依赖项 apue.h 准备的,比如 ...

    原文转自 :http://www.cnblogs.com/ljtknowns/p/5647793.html


    文件目录结构如下

    复制代码
    1 dynamiclibapp.c
    2 Makefile
    3 comm/inc/apue.h
    4 comm/errorhandle.c
    5 dynamiclib/Makefile
    6 dynamiclib/dynamiclib_add.c
    7 dynamiclib/dynamiclib_mul.c
    8 dynamiclib/inc/dynamiclibs.h
    9 dynamiclib/libs/
    复制代码

    1. dynamiclib目录

        dynamiclib/inc/dynamiclibs.h 文件内容如下:

    复制代码
    1 #ifndef __dynamic_libs_h__
    2 #define __dynamic_libs_h__
    3 
    4 #include "apue.h"
    5 int dynamic_lib_func_add(int i1, int i2);
    6 int dynamic_lib_func_mul(int i1, int i2);
    7 
    8 #endif
    复制代码

        dynamiclib/dynamiclib_add.c 文件内容如下:

    复制代码
    1 #include "dynamiclibs.h"
    2 
    3 int dynamic_lib_func_add(int i1, int i2) 
    4 {
    5     int iret = i1 + i2; 
    6     printf("... in .so func, %d add %d,return %d\n", i1, i2, iret);
    7     return iret;
    8 }
    复制代码

        dynamiclib/dynamiclib_mul.c 文件内容如下:

    复制代码
    1 #include "dynamiclibs.h"
    2 
    3 int dynamic_lib_func_mul(int i1, int i2) 
    4 {
    5     int iret = i1 * i2; 
    6     printf("... in .so func, %d multiplys %d, retun %d\n", i1, i2, iret);
    7     return iret;
    8 }
    复制代码

        dynamiclib/Makefile 文件内容如下:

    复制代码
     1 CC       = gcc 
     2 CFLAGS   = -Wall -g -O -fPIC     需要加上 -fPIC
     3 CXXFLAGS = 
     4 INCLUDE  = -I ./inc -I ../comm/inc
     5 TARGET   = libmytest.so
     6 LIBPATH  = ./libs/
     7 
     8 vpath %.h ./inc
     9 
    10 OBJS     = dynamiclib_add.o dynamiclib_mul.o
    11 SRCS     = dynamiclib_add.c dynamiclib_mul.c
    12 
    13 $(OBJS):$(SRCS)
    14    $(CC) $(CFLAGS) $(INCLUDE) -c $^
    15 
    16 all:$(OBJS)
    17    $(CC) -shared -fPIC -o $(TARGET) $(OBJS)    需要加上 -shared -fPIC
    18    mv $(TARGET) $(LIBPATH)
    19 
    20 clean:
    21    rm -f *.o
    22    rm -f $(LIBPATH)*
    复制代码

        以上文件,就可以生成动态库文件 libmytest.so,应用程序以两种方式加载动态库函数,如下

    2. 在编译应用程序时加载动态库

        dynamiclibapp.c 文件内容如下:

    复制代码
     1 #include "apue.h"
     2 #include "dynamiclibs.h"
     3 
     4 int main(int argc, char *argv[])
     5 {
     6     err_msg("step in main\n");
     7     dynamic_lib_func_add(1, 9); 
     8     dynamic_lib_func_mul(1, 9); 
     9     err_msg("step out main\n");
    10 
    11     return 0;
    12 }
    复制代码

        Makefile 文件内容如下:

    复制代码
     1 CC       = gcc 
     2 CFLAGS   = -Wall -O -g
     3 CXXFLAGS = 
     4 INCLUDE  = -I ./comm/inc -I ./dynamiclib/inc
     5 TARGET   = dynamiclibapp
     6 LIBVAR   = -lmytest             指明需要链接动态库 libmytest.so
     7 LIBPATH  = -L./dynamiclib/libs  指明 libmytest.so 的路径
     8 #search paths for errorhandler.c
     9 vpath %.c ./comm
    10 #下行是为依赖项 apue.h 准备的,比如 [errorhandler.o:errorhandler.c apue.h] 里的 apue.h
    11 vpath %.h ./comm/inc
    12 
    13 OBJS     = errorhandler.o dynamiclibapp.o
    14 #下行的 apue.h,可以不必写出来
    15 errorhandler.o:errorhandler.c apue.h
    16    $(CC) $(CFLAGS) $(INCLUDE) -c $^
    17 dynamiclibapp.o:dynamiclibapp.c apue.h
    18    $(CC) $(CFLAGS) $(INCLUDE) -c $^
    19 
    20 all:$(OBJS) $(LIB)
    21    cd ./dynamiclib && make all
    22    $(CC) $(CFLAGS) $(INCLUDE) -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR)
    23    在上行中,在执行编译时,加载了 libmytest.so 中函数
    24 clean:
    25    rm -f *.o
    26    rm -f comm/inc/*.gch
    27    rm -f $(TARGET)
    28    cd ./dynamiclib && make clean
    复制代码

        对于这种方式编译出来的动态库文件,还需要在 /etc/ld.so.conf.d/ 目录中添加 libmytest.so 库文件的路径说明,

        即在 /etc/ld.so.conf.d/ 目录中新建配置文件 mytest.conf,且执行 ldconfig, /etc/ld.so.conf.d/mytest.conf 的文

        件内容为 libmytest.so 库文件的绝对路径,例如:

    1 /home/lijiangtao/dynamiclib/libs

        如果不在编译应用程序时加载动态库文件里的函数,而是改为在应用程序执行时(比如:程序的main函数启动期

        间,或在程序执行期间)加载 libmytest.so 里函数,那么就可以不需在 /etc/ld.so.conf.d/ 目录中配置 libmytest.so

        路径,具体如下所述。

    3. 在应用程序执行时加载动态库

        dynamiclibapp.c 文件内容如下:

    复制代码
     1 #include "apue.h"
     2 #include "dynamiclibs.h"
     3 #include <dlfcn.h>
     4 
     5 typedef int (*fp_lib_add)(int, int);
     6 typedef int (*fp_lib_mul)(int, int);
     7 typedef void* dlhandle;
     8 
     9 dlhandle      dll      = NULL;
    10 fp_lib_add    func_add = NULL;
    11 fp_lib_mul    func_mul = NULL;
    12 
    13 dlhandle load_dynamic_func(char *psopath, fp_lib_add *padd, fp_lib_mul *pmul);
    14 
    15 int main(int argc, char *argv[])
    16 {
    17     char *pso = "/home/lijiangtao/dynamiclib/libs/libmytest.so";//指定 .so 路径
    18     dll = load_dynamic_func(pso, &func_add, &func_mul);//程序执行时,加载动态函数
    19     err_msg("step in main\n");
    20     func_add(1, 9);//执行 add 函数
    21     func_mul(1, 9);//执行 mul 函数
    22     err_msg("step out main\n");
    23 
    24     return 0;
    25 }
    26 
    27 dlhandle load_dynamic_func(char *psopath, fp_lib_add *padd, fp_lib_mul *pmul)
    28 {
    29     if(NULL == psopath ||'\0' == psopath[0])
    30         return NULL;
    31     char *perrormsg = NULL;
    32     dlhandle dllhandle = dlopen(psopath, RTLD_LAZY);
    33     if(NULL == dllhandle) 
    34     {   
    35         printf("%s\n", dlerror());
    36         return NULL;
    37     } 
    38     if(NULL != padd)
    39     {
    40         *padd = dlsym(dllhandle, "dynamic_lib_func_add");//加载 add 函数
    41         perrormsg = dlerror();
    42         if(NULL != perrormsg)
    43             printf("%s\n", perrormsg);
    44     }
    45     if(NULL != pmul)
    46     {
    47         *pmul = dlsym(dllhandle, "dynamic_lib_func_mul");//加载 mul 函数
    48         perrormsg = dlerror();
    49         if(NULL != perrormsg)
    50             printf("%s\n", perrormsg);
    51     }
    52     return dllhandle;
    53 }
    复制代码

        Makefile 文件内容如下:

    复制代码
     1 CC       = gcc 
     2 CFLAGS   = -Wall -O -g
     3 CXXFLAGS = 
     4 INCLUDE  = -I ./comm/inc -I ./dynamiclib/inc
     5 TARGET   = dynamiclibapp
     6 LIBVAR   = -ldl    需要链接 libdl.so 库
     7 LIBPATH  = 
     8 #search paths for errorhandler.c
     9 vpath %.c ./comm
    10 #下行是为依赖项 apue.h 准备的,比如 [errorhandler.o:errorhandler.c apue.h] 里的 apue.h
    11 vpath %.h ./comm/inc
    12 
    13 OBJS     = errorhandler.o dynamiclibapp.o
    14 #下行的 apue.h,可以不必写出来
    15 errorhandler.o:errorhandler.c apue.h
    16    $(CC) $(CFLAGS) $(INCLUDE) -c $^
    17 dynamiclibapp.o:dynamiclibapp.c apue.h
    18    $(CC) $(CFLAGS) $(INCLUDE) -c $^
    19 
    20 all:$(OBJS) $(LIB)
    21    cd ./dynamiclib && make all
    22    $(CC) $(CFLAGS) -rdynamic $(INCLUDE) -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR)
    23    上行,执行编译时并没有加载动态接口函数,而是在应用程序执行时加载的;需要 -rdynamic 选项,
          以确保 dlopen 这些接口可用
    24 clean: 25 rm -f *.o 26 rm -f $(TARGET) 27 cd ./dynamiclib && make clean
    复制代码

        对于这种方式编译出来的动态库文件,不需要在 /etc/ld.so.conf.d/ 目录中配置 libmytest.so 库文件的路径说明


    展开全文
  • gcc 编译链接生成sqlite动态库和静态库动态库 gcc -o libsqlite3.so -fPIC --shared sqlite3.c静态库: gcc -c sqlite3.c -o sqlite3.o(临时文件,用后可删除) ar -r sqlite3.a sqlite3.o链接动态库: g++ ...
    gcc 编译链接生成sqlite动态库和静态库
    动态库
        gcc -o libsqlite3.so -fPIC --shared sqlite3.c
    静态库:
        gcc -c sqlite3.c -o sqlite3.o(临时文件,用后可删除)
        ar -r sqlite3.a sqlite3.o
    链接动态库:
        g++ filename.cpp -L ./ -lsqlite3 -lpthread -ldl

    链接静态库:
        g++ filename.cpp sqlite3.a -lpthread -ldl
    QT连接静态库
        LIBS += /home/wangpadnong/Desktop/sqlite/sqlite3.a
        LIBS += -lpthread
        LIBS += -ldl
    展开全文
  • 1、把头文件及C文件编译成*.o的文件 一般的命令:  gcc -c -fPIC x.c  ... -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足
  • gcc编译链接动态库时,很有可能编译通过,但是执行时,找不到动态链接库,那是 因为-L选项指定的路径只在编译时有效,编译出来的可执行文件不知道-L选项后面的值, 当然找不到。可以用ldd 看看是不有 ‘not found...
  • 我们调用动态链接库有两种方法:一种是编译的时候,指明所依赖的动态链接库,这样loader可以在程序启动的时候,来所有的动态链接映射到内存中;一种是在运行过程中,通过dlopen和dlfree的方式加载动态链接库动态将...
  • 1、Linux静态库和动态库的命名规则 静态函数库 静态库的名字一般是libxxx.a,利用静态库编译生成的文件比较大,因为整个静态库所有的数据都会被整合进目标代码中。 a)优点 编译后,可执行文件不需要外部支持; ...
  • 使用JNI技术时,需要将第三方提供的静态库编译成动态库,在编译过程中遇到问题,使用gcc进行编译链接: gcc -I/home/jichenxiao/java/jdk1.8.0_11/include -I/home/jichenxiao/java/jdk1.8.0_11/include/linux -...
  • 本示例演示了需链接动态库静态库,且需要先编译动态库静态库的makefile编写方式,makefile文件内部有详细的注释,目录下也有介绍文档,希望大家在遇到这种情况时,把本示例稍作修改,就可以运行起来
  • Makefile链接静态库.a编译动态库.so

    千次阅读 2020-03-18 10:00:03
    #静态库编译 BIN=liba.a CC=gcc AR=ar rcs CFLAGS= -shared -fPIC LIBS= INCS=-I. SRCS:=$(wildcard *.c) COBJS:=$(SRCS:.c=.o) all:$(BIN) $(COBJS) : %.o: %.c $(CC) -c $< -o $@ $(INCS) $(CFLAGS) $(LIBS...
  • g++编译动态链接编译简单的动态链接库代码与文件格式编译与调用1、生成动态链接库2、编译调用生成的动态库3、添加环境变量路径4、运行编译依赖第三方库(opencv)的动态链接库代码与文件格式编译与调用 编译简单的...
  • Linux下编译boost动态链接库和静态链接库   1. 先去Boost官网下载需要的Boost版本, 我下载的是boost_1_55_0版本, 然后解压文件。   2. 使用cd命令进入解压后目录: cd boost_1_55_0, 执行下面的命令:  $./...
  • 将静态库编译动态库

    千次阅读 2019-07-12 13:42:12
    最近做一个东西,需要采集硬件设备的音视频数据,然后做编码。以前做过编码部分直接拽过来使用,只写硬件采集部分即可。...编码部分使用ffmpeg4的动态库链接位置自己指定 将硬件采集代码设计为...
  • 依赖静态库,编译生成动态库

    千次阅读 2017-03-09 18:50:34
    直入主题,目标是依赖静态库,编译动态库,最终生成二进制文件。 文件目录如下: 文件内容组织: add.c #include "add.h" int add(int a, int b) { int c = a + b; return c; } add.h #ifndef __ADD__ #...
  • 无聊,遂准备写一篇博客,介绍一下C和C++运行,只因发现工作几年的人对此一知半解的大有人在。    在使用VC构建项目时,经常会遇到下面的链接错误:      初学者面对这些错误常常不知所错:...
  • 1.动态库和静态库简介静态库在程序链接的时候会自动的链接到程序里,所以一旦编译完成,静态库就不需要了,静态库以.a结尾。 动态库编译时不会被连接到目标代码中,而是在程序运行时才被载入,动态库以.so结尾。 ...
  • MinGW编译环境dos下gcc编译静态动态库

    千次阅读 2017-08-04 23:13:35
    动态库生成指令:GCC -...-shared --告诉gcc hello.c 文件需要编译动态库 -Wl表示后面的内容是ld参数,需要传递给ld --out-implib,test.a表示让ld生成一个test.a的静态库 --out-implib,test.lib生成一个导
  • 根据链接时期的不同,库又有静态库和动态库之分,有别于静态库,动态库链接是在程序执行的时候被链接的 1 库的分类 根据链接时期的不同,库又有静态库和动态库之分。 静态库是在链接阶段被链接的...
  • g++ 编译动态链接库和静态链接库

    千次阅读 2014-12-24 09:59:56
    现在我有hello1.cpp和hello2.cpp两个文件,现在我要生成动态链接库libhello.so和静态链接库libhello.a。以下为步骤: 1.生成动态链接库: g++ -m32 hello1.cpp hello2.cpp -fPIC -shared -o ../lib/linux32/...
  • 1.静态编译:编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分...2.动态编译: 动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令。所以其优点一
  • Linux下编译动态链接库与使用详解

    千次阅读 2016-08-12 15:55:03
    动态库 区别:在于代码被载入的时刻不同。静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小 库的存储...
  • 动态库*.so在linux下用c和c++编程时经常会碰到,最近在网站找了几篇文章介绍动态库编译链接,总算搞懂了这个之前一直不太了解得东东,这里做个笔记,也为其它正为动态库链接库而苦恼的兄弟们提供一点帮助。...
  • 大佬们,怎样把libc.a的内容静态编译动态库里呀?比如我动态库里有一个printf,我要怎么把printf静态编译进去?   **就比如我编写了一个.c文件,我想把它编译成共享,在我这个.c中我使用了printf函数...
  • vs2015使用MinGW编译动态库

    千次阅读 2017-11-04 13:48:05
    工作过程中经常会遇到使用其他开源项目的情况,有些开源项目有vs工程,比如opus,可以直接编译出vs可识别的动态库或静态库,但有些开源项目则没有,此时想要做的就是利用该源代码编译一个或多个库来使用。...
  • g++编译动态链接库

    千次阅读 2014-03-21 19:49:10
    编译.so文件 g++ say.cpp -o libsay.so -shared 连接静态 g++ TestRun.cpp -o TestRun -L. -lsay 注意得时libsay.so得写成-lsay 即libfoo.so得写成-lfoo
  • gcc编译链接库并调用链接的方法

    千次阅读 2016-01-06 14:21:50
    linux系统下gcc编译静态链接库动态链接库的方法

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 214,507
精华内容 85,802
关键字:

编译链接动态库