文章出处:http://www.ibm.com/developerworks/cn/linux/l-cn-linklib/
简介:Linux应用开发通常要考虑三个问题,即:1)在Linux应用程序开发过程中遇到过标准库链接在不同Linux版本下不兼容的问题;2)在Linux静态库的制作过程中发现有别于 Windows下静态库的制作方法;3)在Linux应用程序链接第三方库或者其他静态库的时候发现链接顺序的烦人问题。本文就这三个问题针对Linux下标准库链接和如何巧妙构建archive(*.a)展开相关介绍。
两个要知道的基本知识
Linux应用程序因为Linux版本的众多与各自独立性,在工程制作与使用中必须熟练掌握如下两点才能有效地工作和理想地运行。
1. Linux下标准库链接的三种方式(全静态,半静态(libgcc, libstdc++),全动态)及其各自利弊。
2. Linux下如何巧妙构建archive(*.a),并且如何设置链接选项来解决gcc比较特别的链接库的顺序问题。
三种标准库链接方式选项及对比
为了演示三种不同的标准库链接方式对最终应用程序产生的区别,这里用了一个经典的示例应用程序Hello World做演示,见清单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
上述三种标准库链接方式中,比较特殊的是半静态链接方式,主要在于其还需要在链接前增加额外的一个步骤:
ln -s 'g++ -print-file-name=libstd++.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、所示:
通过上述三图,可以清楚地看到,当用全静态标准库的链接方式时,所生成的可执行文件最终不依赖任何的动态标准库,而全动态标准库的链接方式会导致最终应用程序可执行文件依赖于所有用到的标准动态库。
区别于上述两种方式的半静态链接方式则有针对性地将libgcc和libstdc++两个标准库非动态链接。
(对比图2与图3,可见在图3中这两个标准库的动态依赖不见了)
从实际应用当中发现,最理想的标准库链接方式就是半静态链接,通常会选择将libgcc与libstdc++这两个标准库静态链接,从而避免应用程序在不同的Linux版本间标准库依赖不兼容的问题发生。
维度二:最终生成的可执行文件大小(使用size命令进行分析)
size简介:该命令用于显示出可执行文件的大小
涉及语法:size objfile...
其他详细说明请参见man说明。
三种标准库链接方式最终产生的应用程序的可执行文件的大小具体差异见图4、图5、图6所示:
通过上述三图可以看出,最终可执行文件的大小随最终所依赖的标准动态库的数量增加而减小。
从实际应用当中发现,最理想的是半静态链接方式,因为该方式能够在避免应用程序与不同Linux版本间标准库依来不兼容的问题发生的同时,使最终生成的可执行文件大小最小化。
示例链接选项中所涉及命令(引用GCC原文):
-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中可以得知,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下静态库的制作。
这样的demo.a当被链接入某个工程时,所有在TXmlParser.o和xmlparser.o定义的符号都不会被发现,从而会导致连接错误,提示无法找到对应的符号。显然,通过图8方式创建的静态库是不正确的。
正确的方式有两种:
1. 将所有静态库中包含的对象文件提取出来然后重新打包生成新的静态库文件。
2. 用一种更加灵活的方式创建新的静态库文件: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才是我们想要的结果。
Linux静态库链接顺序问题及解决方法:
正如GCC手册中提到的那样:
It makes a different 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的工程师提供有用的帮助。