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  指出库名字
目录
相关文章
|
10月前
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
1896 141
|
12月前
|
JavaScript
Threejs实现PMD模型眨眼说话等功能
这篇文章详细介绍了如何在Three.js中实现PMD模型的眨眼和说话等动态效果,通过控制模型的关键帧来模拟面部表情的变化。
330 0
Threejs实现PMD模型眨眼说话等功能
|
12月前
|
传感器 物联网 区块链
未来已来:探索新兴技术的发展趋势与应用场景
【10月更文挑战第2天】本文将深入探讨区块链技术、物联网(IoT)以及虚拟现实(VR)等新兴技术的最新发展趋势和潜在应用场景。我们将通过具体案例,分析这些技术如何影响我们的生活和工作方式,并讨论它们在未来可能带来的变革。文章旨在为读者提供一个关于这些技术如何塑造我们未来的清晰视角。
|
安全 数据安全/隐私保护 Python
版权保卫战的新武器!揭秘数字水印如何成为知识产权的守护神!
【8月更文挑战第22天】数字水印技术在知识产权保护中至关重要。它通过在数字媒体中嵌入不可见信息(如版权标识),在不影响原内容的前提下实现作品的版权保护、防篡改及非法分发追踪。本文将概述数字水印的概念、技术原理(包括空间域与频域方法),并提供Python代码示例展示水印的嵌入与提取过程。此外,还将分享一个出版社如何运用数字水印成功维护自身版权的真实案例,以此展现数字水印在实际应用中的价值与潜力。
561 0
|
安全 Windows
Windows 无法连接打印机,请检查打印机名并重试。如果这是网络打印机,请确保打印机已打开,并且打印机地址正确。报错代码:0x00000709
Windows 无法连接打印机,请检查打印机名并重试。如果这是网络打印机,请确保打印机已打开,并且打印机地址正确。报错代码:0x00000709
Windows 无法连接打印机,请检查打印机名并重试。如果这是网络打印机,请确保打印机已打开,并且打印机地址正确。报错代码:0x00000709
|
监控 安全 网络安全
云端防御策略:在云计算中确保数据安全与隐私
【4月更文挑战第6天】 随着企业和个人日益依赖云服务,数据安全性和隐私保护成为不容忽视的挑战。本文探讨了云计算环境中面临的主要网络安全威胁,并提出了综合性的安全措施来加强防护。我们将从云服务的基础知识出发,分析安全风险,并深入讨论如何通过加密技术、身份验证、访问控制以及持续监控等手段来提高数据的安全性。文章的目标是为读者提供一套实用的策略框架,以便在享受云计算带来的便利时,能够有效地保障信息安全。
|
XML 数据格式
鸿蒙开发(5)---Image组件
鸿蒙开发(5)---Image组件
897 1
鸿蒙开发(5)---Image组件
|
机器学习/深度学习 存储 NoSQL
X-SIMD高性能跨平台向量化加速库
X-SIMD是平头哥基于开源SIMDe开发的一个header-only C程序库,提供了一种简单易用的跨平台SIMD程序优化方案,旨在为不支持SIMD指令集的平台提供SIMD支持。X-SIMD可以帮助开发者快速完成应用软件迁移arm平台,减少用户重新编写SIMD算法工作量。
|
SQL 网络协议 关系型数据库
|
vr&ar 图形学 异构计算
GLTF编辑器如何合并相同材质的Mesh
建议在创建模型时尽量避免过多使用相同的材质。可以考虑使用材质实例化或者共享材质的方式,来降低模型中的材质数量,并优化渲染及文件大小等方面的性能
343 0