技巧:Linux 动态库与静态库制作及使用详解
技巧:Linux 动态库与静态库制作及使用详解
标准库的三种连接方式及静态库制作与使用方法
Linux 应用开发通常要考虑三个问题,即:1)在 Linux 应用程序开发过程中遇到过标准库链接在不同 Linux 版本下不兼容的问题; 2)在 Linux 静态库的制作过程中发现有别于 Windows 下静态库的制作方法;3)在 Linux 应用程序链接第三方库或者其他静态库的时候发现链接顺序的烦人问题。本文就这三个问题针对 Linux 下标准库链接和如何巧妙构建 achrive(*.a) 展开相关介绍。
两个要知道的基本知识
Linux 应用程序因为 Linux 版本的众多与各自独立性,在工程制作与使用中必须熟练掌握如下两点才能有效地工作和理想地运行。
- Linux 下标准库链接的三种方式(全静态 , 半静态 (libgcc,libstdc++), 全动态)及其各自利弊。
- Linux 下如何巧妙构建 achrive(*.a),并且如何设置链接选项来解决 gcc 比较特别的链接库的顺序问题。
三种标准库链接方式选项及对比
为了演示三种不同的标准库链接方式对最终应用程序产生的区别, 这里用了一个经典的示例应用程序 HelloWorld 做演示,见 清单 1 HelloWorld。 整个工程可以在文章末尾下载。
清单 1. HelloWorld
#include <stdio.h>
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char* argv[])
{
printf("HelloWorld!(Printed by printf)\n");
cout<<"HelloWorld!(Printed by cout)"<<endl;
return 0;
}
三种标准库链接方式的选项及区别见 表 1
表 1. 三种标准库链接方式的选项及区别
| 标准库连接方式 | 示例连接选项 | 优点 | 缺点 |
|---|---|---|---|
| 全静态 | -static -pthread -lrt -ldl | 不会发生应用程序在 不同 Linux 版本下的标准库不兼容问题。 | 生成的文件比较大, 应用程序功能受限(不能调用动态库等) |
| 全动态 | -pthread -lrt -ldl | 生成文件是三者中最小的 | 比较容易发生应用程序在 不同 Linux 版本下标准库依赖不兼容问题。 |
| 半静态 (libgcc,libstdc++) | -static-libgcc -L. -pthread -lrt -ldl | 灵活度大,能够针对不同的标准库采取不同的链接策略, 从而避免不兼容问题发生。 结合了全静态与全动态两种链接方式的优点。 |
比较难识别哪些库容易发生不兼容问题, 目前只有依靠经验积累。 某些功能会因选择的标准库版本而丧失。 |
上述三种标准库链接方式中,比较特殊的是 半静态链接方式,主要在于其还需要在链接前增加额外的一个步骤:
ln -s `g++ -print-file-name=libstdc++.a`,作用是将 libstdc++.a(libstdc++ 的静态库)符号链接到本地工程链接目录。
-print-file-name 在 gcc 中的解释如下:
-print-file-name=<lib> Display the full path to library <lib>
为了区分三种不同的标准库链接方式对最终生成的可执行文件的影响,本文从两个不同的维度进行分析比较:
维度一:最终生成的可执行文件对标准库的依赖方式(使用 ldd 命令进行分析)
ldd 简介:该命令用于打印出某个应用程序或者动态库所依赖的动态库
涉及语法:ldd [OPTION]... FILE...
其他详细说明请参阅 man 说明。
三种标准库链接方式最终产生的应用程序的可执行文件对于标准库的依赖方式具体差异见 图 1、图 2、图 3所示:
图 1. 全静态标准库链接方式

图 2. 全动态标准库链接方式

图 3. 半静态(libgcc,libstdc++) 标准库链接方式

通过上述三图,可以清楚的看到,当用 全静态标准库的链接方式时,所生成的可执行文件最终不依赖任何的动态标准库,
而 全动态标准库的链接方式会导致最终应用程序可执行文件依赖于所有用到的标准动态库。
区别于上述两种方式的 半静态链接方式则有针对性的将 libgcc 和 libstdc++ 两个标准库非动态链接。
(对比 图 2与 图 3,可见在 图 3中这两个标准库的动态依赖不见了)
从实际应用当中发现,最理想的标准库链接方式就是半静态链接,通常会选择将 libgcc 与 libstdc++ 这两个标准库静态链接,
从而避免应用程序在不同 Linux 版本间标准库依赖不兼容的问题发生。
维度二 : 最终生成的可执行文件大小(使用 size 命令进行分析)
size 简介:该命令用于显示出可执行文件的大小
涉及语法:size objfile...
其他详细说明请参阅 man 说明。
三种标准库链接方式最终产生的应用程序的可执行文件的大小具体差异见 图 4、图 5、图 6所示:
图 4. 全静态标准库链接方式

图 5. 全动态标准库链接方式

图 6. 半静态(libgcc,libstdc++) 标准库链接方式

通过上述三图可以看出,最终可执行文件的大小随最终所依赖的标准动态库的数量增加而减小。
从实际应用当中发现,最理想的是 半静态链接方式,因为该方式能够在避免应用程序于
不同 Linux 版本间标准库依赖不兼容的问题发生的同时,使最终生成的可执行文件大小最小化。
示例链接选项中所涉及命令(引用 GCC 原文):
-llibrary
-l library:指定所需要的额外库
-Ldir:指定库搜索路径
-static:静态链接所有库
-static-libgcc:静态链接 gcc 库
-static-libstdc++:静态链接 c++ 库
关于上述命令的详细说明,请参阅 GCC 技术手册
Linux 下静态库(archive)的制作方式:
涉及命令:ar
ar 简介:处理创建、修改、提取静态库的操作
涉及选项:
t - 显示静态库的内容
r[ab][f][u] - 更新或增加新文件到静态库中
[s] - 创建文档索引
ar -M [<mri-script] - 使用 ar 脚本处理
其他详细说明请参阅 man 说明。
示例情景:
假设现有如 图 7所示两个库文件
图 7. 示例静态库文件

从 图 7中可以得知,CdtLog.a 只包含 CdtLog.o 一个对象文件 , 而 xml.a 包含 TXmlParser.o 和 xmlparser.o 两个对象文件
现将 CdtLog.o 提取出来,然后通过 图 8方式创建一个新的静态库 demo.a,可以看出,demo.a 包含的是 CdtLog.o 以及 xml.a,
而不是我们所预期的 CdtLog.o,TXmlParser.o 和 xmlparser.o。这正是区别于 Windows 下静态库的制作。
图 8. 示例静态库制作方式 1

这样的 demo.a 当被链接入某个工程时,所有在 TXmlParser.o 和 xmlparser.o 定义的符号都不会被发现,从而会导致链接错误,
提示无法找到对应的符号。显然,通过图 8 方式创建 Linux 静态库是不正确的。
正确的方式有两种:
- 将所有静态库中包含的对象文件提取出来然后重新打包成新的静态库文件。
- 用一种更加灵活的方式创建新的静态库文件:ar 脚本。
显然,方式 1 是比较麻烦的,因为涉及到太多的文件处理,可能还要通过不断创建临时目录用于保存中间文件。
推荐使用如 清单 2 createlib.sh所示的 ar 脚本方式进行创建:
清单 2 createlib.sh
rm demo.a rm ar.mac echo CREATE demo.a > ar.mac echo SAVE >> ar.mac echo END >> ar.mac ar -M < ar.mac ar -q demo.a CdtLog.o echo OPEN demo.a > ar.mac echo ADDLIB xml.a >> ar.mac echo SAVE >> ar.mac echo END >> ar.mac ar -M < ar.mac rm ar.mac
如果想在 Linux makefile 中使用 ar 脚本方式进行静态库的创建,可以编写如 清单 3 BUILD_LIBRARY所示的代码:
清单 3 BUILD_LIBRARY
define BUILD_LIBRARY
$(if $(wildcard $@),@$(RM) $@)
$(if $(wildcard ar.mac),@$(RM) ar.mac)
$(if $(filter %.a, $^),
@echo CREATE $@ > ar.mac
@echo SAVE >> ar.mac
@echo END >> ar.mac
@$(AR) -M < ar.mac
)
$(if $(filter %.o,$^),@$(AR) -q $@ $(filter %.o, $^))
$(if $(filter %.a, $^),
@echo OPEN $@ > ar.mac
$(foreach LIB, $(filter %.a, $^),
@echo ADDLIB $(LIB) >> ar.mac
)
@echo SAVE >> ar.mac
@echo END >> ar.mac
@$(AR) -M < ar.mac
@$(RM) ar.mac
)
endef
$(TargetDir)/$(TargetFileName):$(OBJS)
$(BUILD_LIBRARY)
通过 图 9,我们可以看到,用这种方式产生的 demo.a 才是我们想要的结果。
图 9. 巧妙创建的静态库文件结果

Linux 静态库链接顺序问题及解决方法:
正如 GCC 手册中提到的那样:
It makes a difference where in the command you write this option; the linker
searches and processes libraries and object files in the order they are specified.
Thus, ‘ foo.o -lz bar.o ’ searches library ‘ z ’ after file ‘ foo.o ’ but before
‘ bar.o ’ . If ‘ bar.o ’ refers to functions in ‘ z ’ , those functions may not be loaded.
为了解决这种库链接顺序问题,我们需要增加一些链接选项 :
$(CXX) $(LINKFLAGS) $(OBJS) -Xlinker "-(" $(LIBS) -Xlinker "-)" -o $@
通过将所有需要被链接的静态库放入 -Xlinker "-(" 与 -Xlinker "-)" 之间,可以是 g++ 链接过程中,
自动循环链接所有静态库,从而解决了原本的链接顺序问题。
涉及链接选项:-Xlinker
-Xlinker option
Pass option as an option to the linker. You can use this to supply system-specific
linker options which GCC does not know how to recognize.
小结
本文介绍了 Linux 下三种标准库链接的方式及各自利弊,同时还介绍了 Linux 下静态库的制作及使用方法,相信能够给
大多数需要部署 Linux 应用程序和编写 Linux Makefile 的工程师提供有用的帮助。
技巧:Linux 动态库与静态库制作及使用详解的更多相关文章
- linux系统环境下的静态库和动态库的制作
linux系统下的应用编程需要系统提供的库文件,包括静态库或动态库.不管是静态库还是动态库,都是编译好的二进制文件.在我们编译程序时要链接的目标文件,静态库是链接的时候直接编译到程序里,和程序成为一体 ...
- Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名
Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名 转载自:http://b ...
- Linux系统中“动态库”和“静态库”那点事儿【转】
转自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻. ...
- Linux系统中“动态库”和“静态库”那点事儿
摘自http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在 ...
- 自己在linux上编译、链接、动态库和静态库的学习笔记
在平常的项目中,我们都是使用公司要求的makefile.makedebug一类的文件,因此,在编译.链接.生成和链接动态库与静态库的时候,我们只是简单的使用一些已经设置的变量,只是简单的修改.添加一些 ...
- Linux中的动态库和静态库(.a/.la/.so/.o)
Linux中的动态库和静态库(.a/.la/.so/.o) Linux中的动态库和静态库(.a/.la/.so/.o) C/C++程序编译的过程 .o文件(目标文件) 创建atoi.o 使用atoi. ...
- linux中C的静态库和动态库分析
从开始学C语言写第一个"hello world"历程到现在,我依然困惑于到底这个程序完整的执行流程是什么样的.不过,现在我正在尝试一点一点的揭开它的面纱.现在,我尝试分析linux ...
- Linux的动态库与静态库
1.动态库与静态库简介 在实际的软件开发中,为了方便使用一些被重复调用的公共代码,我们经常将这些公共的函数编译成动态库或静态库.我们知道程序一般要经过预处理.编译.汇编和链接这几个步骤才能变成可执行的 ...
- linux动态库与静态库混合连接
1, 在应用程序需要连接外部库的情况下,linux默认对库的连接是使用动态库,在找不到动态库的情况下再选择静态库.使用方式为: gcc test.cpp -L. -ltestlib 如果当前目录有 ...
随机推荐
- 站内信DB设计实现
两年前,万仓一黍在博客园发了两篇关于站内信的设计实现博文,<群发"站内信"的实现>.<群发"站内信"的实现(续)>,其中阐述了他关于站内 ...
- javascript 判断是否是数组
function isArray(object){ return object && typeof object==='object' && typeof object ...
- CentOS 6.3下rsync服务器的安装与配置[转]
CentOS 6.3下rsync服务器的安装与配置 一.rsync 简介 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件,也 ...
- 通过分析 JDK 源代码研究 Hash 存储机制--转载
通过 HashMap.HashSet 的源代码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java 对象放入数组中,只是把对象 ...
- 用switch判断月份的练习
import java.util.Scanner; public class SwitchTest01 { public static void main(String[] args) { Syste ...
- 大数据笔记01:大数据之Hadoop简介
1. 背景 随着大数据时代来临,人们发现数据越来越多.但是如何对大数据进行存储与分析呢? 单机PC存储和分析数据存在很多瓶颈,包括存储容量.读写速率.计算效率等等,这些单机PC无法满足要求. 2. ...
- Nginx配置域名跳转实例
要求:浏览器地址栏输入qj.123.com之后,地址自动变成qj.abc.com 配置nginx跳转 server { listen 80; server_name qj.abc.com qj.123 ...
- Hyper-V下的Linux虚拟机网卡丢失问题原因及解决办法
Hyper-V下的Linux虚拟机网卡丢失问题原因及解决办法 虚拟化大势所趋 公司推行了虚拟化,全部用的是Microsoft Windows 2008 R2 Enterprise with Hyp ...
- Probably at least one of the constraints in the following list is one you don't want.
这个提示并不是出错,不理会它我的程序也没出现什么问题 但是处于强迫症,还是努力寻找解决的方法... 最终发现问题如下: 在xib各种绘制和添加约束的UITableViewCell之后,在某一特定情况想 ...
- PHP include 和 require
PHP include 和 require 语句 在 PHP 中,您可以在服务器执行 PHP 文件之前在该文件中插入一个文件的内容. include 和 require 语句用于在执行流中插入写在其他 ...