最近有幸阅读了《高级C/C++编译技术》深受启发,该书深刻浅出地讲解了构建过程(编译、连接)中的各类细节,从多个角度展现了程序与库文件或代码的集成方法,提出了面向代码复用和系统集成的软件架构设计方法,以及系统开发过程当中疑难问题的解决方案。
如下将回头记录下其中的关键要点,以便后面查阅。
(1)静态库:lib+<library name>. + alinux
(2)动态库:lib+<<library name> + .so + <library version information>ios
(3)动态库的版本信息算法
dynamic libaray version information = <M>.<m>.<p>缓存
M:主版本号架构
m:次版本号函数
p:补丁(很小的代码改动)版本号工具
(4)动态库的soname测试
soname = lib+<libaray name>+.so+Mspa
动态库的soname一般由链接器嵌入二进制文件的专有ELF字段中,一般用特定的链接器选项,将表示库soname的字符串传递给连接器操作系统
g++ -shared <list of object files> -Wl, -soname, libfoo.so.1 -o libfoo.so.1.0.0
注意:(1)-Wl选项告诉编译器将后面的参数传递给连接器
(2)凡是间接调用连接器时,咱们须要在连接器参数前追加“Wl,”
在Linux中用-L和-l选项来指定构建过程当中库文件路径
(1)将完整的库文件路径分红两个部分:目录路径和库文件名
(2)将目录路径添加到-L链接器选项后面,并传递给连接器
(3)将库文件名添加到-l参数后面,并传递给链接器
g++ main.o -L../sharedlib -lwokingdemo -o demo
在使用g++命令行一次性完整编译连接两个过程时,应在在连接器前添加-Wl,
g++ -Wall -fPIC main.cpp -Wl, -L../sharedlib -Wl, -lworkingdeno -o demo
对于经验不足的linux软件工程师常常会遇到找到不动态库的状况而不知所措,主要是对如下内容不够了解。
动态库运行时搜索算法由一组规则约束,按照优先级从高到低列出
(1)预加载库
毫无疑问,预加载库应该拥有最高的搜索优先级,装载器会首先加载这些库,而后才开始搜索其它库,有两种方法能够指定预加载库
a:经过设置LD_PRELOAD环境变量
export LD_PRELOAD=/home/fate/shareddir/libtest.so:$LD_PRELOAD
b:经过修改、etc/ld.so.preload文件
指定预加载库并不符合标准的设计规范。相反,该方案仅用于特殊状况,好比设计压力测试、诊断已经对原始代码的紧急补丁等
(2)rpath
rpath和runpath均可供咱们使用,可是runpath在运行时搜索优先级列表中赋予了更高的优先级,只有在runpath缺失的状况下,rpath才是linux装载器剩余的搜索路径信息中具备最高优先级的。
但若是ELF二进制文件的runpath(DT_RUNPATH)字段是非空的,那么rpath会被忽略
g++ -Wl, -R/home/fate/shared/ -ltestlibrary
或者,也能够用LD_RUN_PATH环境变量来设置rpath
export LD_RUN_PATH=/home/fate/shared:$LD_RUN_PATH
(3)LD_LIBARAY_PATH
从库搜索概念发展初期开始,开发人员就但愿可使用一种临时应急的有效机制来验证它们的设计,经过特定的环境变量(LD_LIBRARY_PATH)就能解决咱们遇到的问题,当没有rpath时,该路径就是路径搜索信息中优先级最高的
export LD_LIBRARY_PATH=/home/fate/shared/:$LD_LIBRARY_PATH
注意:该机制只应用于实验目的,软件产品的产品版本不该该依赖于这种机制
(4)runpath
设置runpath的方法和设置rpath的方法很是类似,为了传递-R或-rpath连接器选项,须要使用额外的--enable-new-dtags连接选项
g++ -Wl,-R/home/fate/shared/ -Wl, --enable-new-dtags -ltestlibrary
(5)ldconfig缓存
一种标准的代码部署过程是基于运行linux的ldconfig工具,ldconfig会将指定的目录路径插入动态库搜索列表中,该列表维护在文件/etc/ld.so.conf中。一样地,系统会扫描新加入的目录路径,其结果是将发现的库文件名添加到库文件名列表中,该表维护在/etc/ld.so.cache文件中
/lib和/usr/lib是linux操做系统保存动态库的两个默认路径
总的来讲,优先级方案能够概括为如下两种版本:
(1)若是指定了RUNPATH(即LD_RUNPATH字段非空)
a. LD_LIBRARY_PATH
b. runpath(LD_RUNPATH)
c. ld.so.cache
d. 默认路径
(2)若是没有指定runpath
a. 被加载库的RPATh,而后是二进制文件的RPATH,直到可执行文件或者动态库将这些库所有加载完毕为止
b. LD_LIBRAYR_PATH
c. ld.so.cache
d. 默认路径
linux_so.h
#pragma once #ifdef __cplusplus extern "C" { #endif void fun(); #ifdef __cplusplus } #endif
linux_so.c
#include "linux_so.h" #include <iostream> using namespace std; void fun() { cout << "print fun" << endl; }
main.c
#include "stdio.h" #include "linux_so.h" int main() { fun(); return 0; }
编译、连接
g++ -Wall -g -o0 -fPIC -shared linux_so.c -o liblinux_so.so
g++ -Wall -g -o0 -fPIC main.c -Wl,-L./ -Wl,-llinux_so -o out
export LD_LIBRARY_PATH=/home/fate/sharedir/dlltest/:$LD_LIBRAYR_PATH
输出