static and dynamic libraries

简介:
在将一堆c文件生成二进制可执行文件时, 有4个阶段, 最后一个阶段是link阶段, 这其实不是gcc做的事情(gcc只是一个compiler), 而是linker做的, 在linux里面是ld命令 . gcc调用ld去将object file link输出到bin file的. ld是隐藏在幕后干活的.

static library(linux中可以使用ar将对象文件归档到静态库里面,  文件名 以lib开头, .a结尾) : 
在link过程,使用静态库的话,在.a文件中需要用到的object file将打包到二进制文件中. 没有用到的object file不会link进去.
static and dynamic libraries - 德哥@Digoal - The Heart,The World.
 

dynamic library(linux中称为shared object, 使用gcc -shared 将object file(s)打包到so文件. 文件名 以lib开头, . so结尾.) : 
在link过程不会把对象文件合并到bin文件, 而只是放置占位符, 当程序执行的时候, 再加载相关的对象文件.
static and dynamic libraries - 德哥@Digoal - The Heart,The World.

static and dynamic libraries - 德哥@Digoal - The Heart,The World.
  
编译时用到静态库的程序拷贝到其他机器上使用时不会因为缺少对象文件而异常。 用到动态库的, 把程序拷贝到其他机器上同时还需要把so文件拷贝过去, 否则不能使用. 
动态库的好处是, 当用到的动态库需要变更时, 不需要重新编译程序, 只需要替换so 文件即可. 另外, 对于比较大的程序, 更适合使用动态库, 因为编译的程序文件不会太大.

【创建】
1. 静态库 : 
c文件 : 
[root@db-172-16-3-150 zzz]# cat a.c
#include <stdio.h>
#include "a.h"

void display() {
  fprintf(stdout, "this is display function in a.c\n");
}

头文件 : 
[root@db-172-16-3-150 zzz]# cat a.h
void display();

生成静态库 : 
首先要生成object file.

[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g -c ./a.c -o a.o

gcc 简述 : 
DESCRIPTION
       When you invoke GCC, it normally does preprocessing, compilation, assembly and linking.  The "overall options"
       allow you to stop this process at an intermediate stage.  For example, the -c option says not to run the
       linker.  Then the output consists of object files output by the assembler.

这里使用了-c , 所以输出的是对象文件, 没有去调用linker(ld).

接下来要把object file a.o打包到静态库liba.a, 注意命名规则 : 

[root@db-172-16-3-150 zzz]# ar -rcs liba.a ./a.o

查看这个静态库文件包含了哪些对象文件:
[root@db-172-16-3-150 zzz]# ar -t liba.a 
a.o

查看liba.a静态库文件里面的symbols : 
[root@db-172-16-3-150 zzz]# nm liba.a 

a.o:
0000000000000000 T display   // 这个是display函数
                 U fwrite
                 U stdout


2. 动态库 : 
与静态库不一样的地方, 创建object file时需要加-fPIC 参数.
PIC是position-independent code的缩写, 为什么要这么做呢? 原因是和操作系统加载动态库的方式有关, 如果程序调用动态库时内核不是将动态库加载到内存, 而程序要通过offset去获取动态库中的代码段时, 可能会有问题, PIC是要解决这个问题的。
如下 : 

      -fpic
           Generate position-independent code (PIC) suitable for use in a shared library, if supported for the target
           machine.  Such code accesses all constant addresses through a global offset table (GOT).  The dynamic
           loader resolves the GOT entries when the program starts (the dynamic loader is not part of GCC; it is part
           of the operating system).  If the GOT size for the linked executable exceeds a machine-specific maximum
           size, you get an error message from the linker indicating that -fpic does not work; in that case, recompile
           with -fPIC instead.  (These maximums are 8k on the SPARC and 32k on the m68k and RS/6000.  The 386 has no
           such limit.)

           Position-independent code requires special support, and therefore works only on certain machines.  For the
           386, GCC supports PIC for System V but not for the Sun 386i.  Code generated for the IBM RS/6000 is always
           position-independent.

       -fPIC
           If supported for the target machine, emit position-independent code, suitable for dynamic linking and
           avoiding any limit on the size of the global offset table.  This option makes a difference on the m68k,
           PowerPC and SPARC.

           Position-independent code requires special support, and therefore works only on certain machines.

接下来创建object file : 
c文件 : 

[root@db-172-16-3-150 zzz]# cat a.c
#include <stdio.h>
#include "a.h"

void display() {
  fprintf(stdout, "this is display function in a.c\n");
}

头文件 : 
[root@db-172-16-3-150 zzz]# cat a.h
void display();

生成object file : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g -fPIC -c ./a.c -o a.o

这个object file 也可以直接使用 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./b.c a.o -o b
[root@db-172-16-3-150 zzz]# ./b
this is display function in a.c


接下来就生成 包含a.o的动态库文件liba.so吧 : 
如果生成a.o时未加-fPIC, 创建liba.so将报错 : 

[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g -shared a.o -o liba.so
/usr/bin/ld: a.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
a.o: could not read symbols: Bad value
collect2: ld returned 1 exit status
所以必须加上-fPIC : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g -fPIC -c ./a.c -o a.o
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g -shared a.o -o liba.so


【命名规则】
静态库 : 
lib?.a
动态库 : 
lib?.so
static and dynamic libraries - 德哥@Digoal - The Heart,The World.
 

【使用】
不管是动态还是静态库, 编译时都是使用 "-L目录名" "-l库名" 
如果库文件放在系统的库文件目录中(如/usr/lib), 则不需要使用 "-L目录名"指出这个库文件在哪里.
或者配置LD_LIBRARY_PATH(linux)或PATH(windows)
static and dynamic libraries - 德哥@Digoal - The Heart,The World.
 
静态库和动态库在生成bin文件的使用是一样的 : 

[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g b.c -L. -la -o b

注意c文件一定要放在库文件前面, 否则可能会报错. b.c -L. -la 如果改成 -L. -la b.c 就可能报错.
-L 表示库文件在什么目录找.
-l 表示需要用到那个库

如果动态库和静态库都有的情况下会使用哪个呢?
如 : 

-rw-r--r-- 1 root root 6168 Aug 15 10:26 liba.a
-rwxr-xr-x 1 root root 8465 Aug 15 10:56 liba.so


默认使用的是动态库 : 
[root@db-172-16-3-150 zzz]# ll
total 40
-rw-r--r-- 1 root root  110 Aug 15 09:58 a.c
-rw-r--r-- 1 root root   16 Aug 15 09:58 a.h
-rw-r--r-- 1 root root 6064 Aug 15 11:09 a.o
-rw-r--r-- 1 root root   56 Aug 15 09:58 b.c
-rw-r--r-- 1 root root 6208 Aug 15 11:09 liba.a
-rwxr-xr-x 1 root root 8449 Aug 15 11:10 liba.so
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g b.c -L. -la -o b
[root@db-172-16-3-150 zzz]# ./b
./b: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
[root@db-172-16-3-150 zzz]# export LD_LIBRARY_PATH=.
[root@db-172-16-3-150 zzz]# ll

显然使用动态库编译的bin文件b要比使用静态库的小( 8185 bytes ).
total 48
-rw-r--r-- 1 root root  110 Aug 15 09:58 a.c
-rw-r--r-- 1 root root   16 Aug 15 09:58 a.h
-rw-r--r-- 1 root root 6064 Aug 15 11:09 a.o
-rwxr-xr-x 1 root root 8185 Aug 15 11:14 b
-rw-r--r-- 1 root root   56 Aug 15 09:58 b.c
-rw-r--r-- 1 root root 6208 Aug 15 11:09 liba.a
-rwxr-xr-x 1 root root 8449 Aug 15 11:10 liba.so
[root@db-172-16-3-150 zzz]# ./b
this is display function in a.c


使用-static强制静态编译.
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g -static b.c -L. -la -o b
[root@db-172-16-3-150 zzz]# ll
total 640
-rw-r--r-- 1 root root    110 Aug 15 09:58 a.c
-rw-r--r-- 1 root root     16 Aug 15 09:58 a.h
-rw-r--r-- 1 root root   6064 Aug 15 11:09 a.o
-rwxr-xr-x 1 root root 609720 Aug 15 11:14 b
-rw-r--r-- 1 root root     56 Aug 15 09:58 b.c
-rw-r--r-- 1 root root   6208 Aug 15 11:09 liba.a
-rwxr-xr-x 1 root root   8449 Aug 15 11:10 liba.so
[root@db-172-16-3-150 zzz]# ./b
this is display function in a.c
// 修改LD_LIBRARY_PATH后依旧可用使用, 说明却是已经不是动态状态了. 但是文件硕大(609720 bytes). 不建议使用.
[root@db-172-16-3-150 zzz]# export LD_LIBRARY_PATH=/usr/lib
[root@db-172-16-3-150 zzz]# ./b
this is display function in a.c
说明  : 
       -static
           On systems that support dynamic linking, this prevents linking with the shared libraries.  On other sys-
           tems, this option has no effect.


使用静态库编译出来b的大小(9973 bytes) : 
[root@db-172-16-3-150 zzz]# mv liba.so libb.so
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g b.c -L. -la -o b
[root@db-172-16-3-150 zzz]# ll -a
total 64
drwxr-xr-x  2 root root 4096 Aug 15 11:18 .
drwxr-x--- 16 root root 4096 Aug  9 11:22 ..
-rw-r--r--  1 root root  110 Aug 15 09:58 a.c
-rw-r--r--  1 root root   16 Aug 15 09:58 a.h
-rw-r--r--  1 root root 6064 Aug 15 11:09 a.o
-rwxr-xr-x  1 root root 9973 Aug 15 11:18 b
-rw-r--r--  1 root root   56 Aug 15 09:58 b.c
-rw-r--r--  1 root root 6208 Aug 15 11:09 liba.a
-rwxr-xr-x  1 root root 8449 Aug 15 11:10 libb.so


【注意】
1. 有书说动态库的名字修改后不能使用, 如liba.so 改成libb.so 使用-lb编译也无法使用. 实际测试可用使用.

[root@db-172-16-3-150 zzz]# mv liba.so libb.so
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g b.c -L. -lb -o b
[root@db-172-16-3-150 zzz]# ./b
./b: error while loading shared libraries: libb.so: cannot open shared object file: No such file or directory
[root@db-172-16-3-150 zzz]# export LD_LIBRARY_PATH=.
[root@db-172-16-3-150 zzz]# ./b
this is display function in a.c


【参考】
man ar, nm, gcc

ar : 
       r   Insert the files member... into archive (with replacement). This operation differs from q in that any  pre-
           viously existing members are deleted if their names match those being added.

           If one of the files named in member... does not exist, ar displays an error message, and leaves undisturbed
           any existing members of the archive matching that name.

           By default, new members are added at the end of the file; but you may use one of the modifiers a, b,  or  i
           to request placement relative to some existing member.

           The  modifier v used with this operation elicits a line of output for each file inserted, along with one of
           the letters a or r to indicate whether the file was appended (no old member deleted) or replaced.
       c   Create  the  archive.   The  specified  archive  is always created if it did not exist, when you request an
           update.  But a warning is issued unless you specify in advance that you expect to create it, by using  this
           modifier.
       s   Write an object-file index into the archive, or update an existing one, even if no other change is made  to
           the  archive.   You  may  use  this  modifier flag either with any operation, or alone.  Running ar s on an
           archive is equivalent to running ranlib on it.
       t   Display a table listing the contents of archive, or those of the files listed in member... that are present
           in  the  archive.  Normally only the member name is shown; if you also want to see the modes (permissions),
           timestamp, owner, group, and size, you can request that by also specifying the v modifier.

           If you do not specify a member, all files in the archive are listed.

           If there is more than one file with the same name (say, fie) in an archive (say b.a), ar t  b.a  fie  lists
           only the first instance; to see them all, you must ask for a complete listing---in our example, ar t b.a.

查看一个静态库文件里面包含了哪些对象文件 : 
[root@db-172-16-3-150 lib]# cd /usr/lib
[root@db-172-16-3-150 lib]# ar -t libz.a
adler32.o
compress.o
crc32.o
gzio.o
uncompr.o
deflate.o
trees.o
zutil.o
inflate.o
infback.o
inftrees.o
inffast.o


查看库文件中的symbol信息 :
[root@db-172-16-3-150 lib]# nm ./libz.a
adler32.o:
0000000000000000 T adler32
0000000000000390 T adler32_combine

compress.o:
0000000000000000 r .LC0
         U _GLOBAL_OFFSET_TABLE_
0000000000000000 T __i686.get_pc_thunk.bx
00000000000000f0 T compress
0000000000000020 T compress2
0000000000000000 T compressBound
         U deflate
         U deflateEnd
         U deflateInit_
.......
这里标记的意思可参考man nm
[root@db-172-16-3-150 zzz]# nm liba.so
0000000000200660 a _DYNAMIC
0000000000200800 a _GLOBAL_OFFSET_TABLE_
                 w _Jv_RegisterClasses
0000000000200638 d __CTOR_END__
0000000000200630 d __CTOR_LIST__
0000000000200648 d __DTOR_END__
0000000000200640 d __DTOR_LIST__
0000000000000628 r __FRAME_END__
0000000000200650 d __JCR_END__
0000000000200650 d __JCR_LIST__
0000000000200828 A __bss_start
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000000570 t __do_global_ctors_aux
00000000000004a0 t __do_global_dtors_aux
0000000000200658 d __dso_handle
                 w __gmon_start__
0000000000200828 A _edata
0000000000200838 A _end
00000000000005a8 T _fini
0000000000000438 T _init
0000000000000480 t call_gmon_start
0000000000200830 b completed.6145
0000000000000550 T display
0000000000200828 b dtor_idx.6147
0000000000000520 t frame_dummy
                 U fwrite@@GLIBC_2.2.5
                 U stdout@@GLIBC_2.2.5

gcc 常用参数 : 
-O3  代码优化,级别3
-Wall  警告
-Wextra  额外警告
-Werror  将警告当做error处理
-g  包含debug信息到bin文件
-L  指出LIBRARY检索目录
-I  指出头文件检索目录
-l  指出库名字
相关文章
|
1月前
|
人工智能 机器人 测试技术
【CMake报错】Cannot specify compile definitions for target “PRIVATE“ which is not built...
【CMake报错】Cannot specify compile definitions for target “PRIVATE“ which is not built...
static library libs/libvpx/libvpx.a is not portable!
static library libs/libvpx/libvpx.a is not portable!
298 0
|
Ubuntu
Could not load dynamic library 'libcudnn.so.8'
Could not load dynamic library 'libcudnn.so.8'
834 0
|
C++
configure: error: libmpfr not found or uses a different ABI (including static vs shared).
配置mpc的时候提示此错误: configure: error: libmpfr not found or uses a different ABI (including static vs shared).
2665 0
|
JSON 数据格式
sonar代码质量检测告警“static“ base class members should not be accessed via derived types
sonar代码质量检测告警“static“ base class members should not be accessed via derived types
721 0
sonar代码质量检测告警“static“ base class members should not be accessed via derived types