CMake和静态库顺序
目录
目录 1
前言 1
方法 1
附1:链接静态库的顺序问题 2
附2:再议GCC编译时的静态库依赖次顺问题 3
附3:gcc链接参数--whole-archive的作用 4
附4:让有些“-l”链接静态库,而另一些链接共享库? 6
附5:相关博文 6
前言
C/C++程序的许多同学被静态库的依赖折腾,因为默认情况下要求被依赖的库放在依赖它的库后面,当一个程序或共享库依赖的静态库较多时,可能会陷入解决链接问题的坑中。如果对静态库不熟悉,需要结构nm等工具来解决顺序问题。
但也可以偷懒,不关心静态库的顺序问题,ld为此提供了start-group和end-group两个选项,让包含在这两者间的静态库顺序可以随意。
方法
以CMake为例,假设程序x依赖三个静态库:libX1.a、libX2.a和libX3.a,而libX2.a又依赖libX1.a,libX3.a依赖libX2.a和libX1.a,正常情况下的CMakeLists.txt格式如下:
add_executable( x x.cpp ) target_link_libraries( x libX1.a libX2.a libX3.a ) |
上面的写法libX1.a、libX2.a和libX3.a的顺序不能变,只能按上面的先后顺序。如果去掉顺序的烦恼和痛苦,可以采用如下的写法:
target_link_libraries( x -Wl,--start-group libX1.a libX3.a libX2.a -Wl,--end-group ) 或 target_link_libraries( x -Wl,--start-group libX3.a libX2.a libX1.a -Wl,--end-group ) 都可以,完全不用关心顺序。 |
前面说了start-group和end-group是ld的选项,是链接选项,不是gcc/g++的编译选项,直接命令行或其它编译方式也可以使用,比如命令行方式:
g++ -g -o x x.cpp -Wl,--start-group libX2.a libX1.a libX3.a -Wl,--end-group |
附1:链接静态库的顺序问题
在链接静态库时,如果多个静态库之间存在依赖关系,则有依赖关系的静态库之间存在顺序问题,这个在使用静态库时需要注意,否则会报符号找不到问题。举例,libb.a依赖于是liba.a,而可执行文件test只直接依赖于libb.a,则链接选项应当为“-b -a”,而不是“-a -b”,否则会报liba.a中的某些符号找不到。
gcc -c a.c ar cr liba.a a.o gcc -c b.c ar cr libb.a b.o |
虽然libb.a使用到了liba.o中的一些函数,但并不会将它们的定义包含进来,所以在链接test时需要指定这两个库。
另外,在编译libb.a时是不指定liba.a的,因为编译一个静态库不会使用到链接选项,而只需要指定需要依赖的头文件路径即可。
-Wl的使用:
-Wl表示后面的参数传递给链接器,其中l是linker的意思。 链接时指定共享库的搜索路径(类似于设置LD_LIBRARY_PATH): -Wl,-rpath=/usr/local/abc:/data/abc 以上也可以分开写: -Wl,-rpath=/usr/local/abc -Wl,-rpath=/data/abc 部分库链接它的静态库,部分库链接它的共享库: -Wl,-static -lb -Wl,-call_shared -la -lz 指定链接器: -Wl,-dynamic-linker /lib/ld-linux.so.2 -e _so_start 指定导出的符号: -Wl,--export-dynamic,--version-script,exports.lds exports.lds的格式可以为: { global: foo; }; 指定共享库的soname: -Wl,--export-dynamic,--version-script,exports.lds,-soname=libqhttpd.so -rpath 增加共享库搜索路径 --retain-symbols-file表示不丢弃未定义的符号和需要重定位的符号 --export-dynamic 创建一个动态连接的可执行程序时, 把所有的符号加到动态符号表中 |
附2:再议GCC编译时的静态库依赖次顺问题
假设有如三个源代码文件:
$ cat a.cpp void a() { } $ cat b.cpp extern void a(); void b() { a(); // 调用a.cpp中的a() } $ cat x.cpp extern void b(); int main() { b(); // 调用b.cpp中的b() return 0; } |
对应的Makefile文件:
all: x liba.a: a.o libb.a: b.o x: x.o liba.a libb.a # 问题出在这儿 g++ -g -o $@ $^ a.o: a.cpp g++ -g -c $^ b.o: b.cpp g++ -g -c $^ x.o: x.cpp g++ -g -c $^ clean: rm -f a.o b.o x.o x |
使用上面的Makefile编译,将会遇到如下所示的“undefined reference”问题:
g++ -g -c x.cpp g++ -g -c a.cpp g++ -g -c b.cpp g++ -g -o x x.o liba.a libb.a # 改成“g++ -g -o x x.o libb.a liba.a”即可解决 libb.a(b.o): In function `b()': /data/jayyi/gongyi/activities/phonebook/b.cpp:2: undefined reference to `a()' collect2: ld returned 1 exit status make: *** [x] Error 1 |
这个问题的原因是b.cpp依赖a.cpp,gcc要求(实际是ld要求)libb.a须放在liba.a前面,即需要改成:g++ -g -o x x.o libb.a liba.a,也就是被依赖的库需要放在后头。
这是最常规的解决办法,除此之外,只需要加入--start-group和--end-group两个链接参数,即可保持被依赖的库放在前头,也就是改成如下即可:g++ -g -o $@ -Wl,--start-group $^ -Wl,--end-group。
这里的“-Wl,”表示后面跟着的参数是传递给链接器ld的,gcc不关心具体是啥。“--start-group”表示范围的开始;“--end-group”表示范围的结束,是可选的。位于“--end-group”之后的仍然要求被依赖的库放在后头。
附3:gcc链接参数--whole-archive的作用
// a.h
extern void foo(); |
// a.cpp
#include <stdio.h> void foo() { printf("foo\n"); } |
// x.cpp
#include "a.h" int main() { foo(); return 0; } |
// Makefile
all: x x: x.cpp liba.so g++ -g -o $@ $^ liba.so: liba.a g++ -g -fPIC -shared -o $@ $^ #g++ -g -fPIC -shared -o $@ -Wl,--whole-archive $^ -Wl,-no-whole-archive liba.a: a.o ar cru $@ $^ a.o: a.cpp g++ -g -c $^ clean: rm -f x a.o liba.a liba.so |
$ make
g++ -g -c a.cpp ar cru liba.a a.o g++ -g -fPIC -shared -o liba.so liba.a #g++ -g -fPIC -shared -o liba.so -Wl,--whole-archive liba.a -Wl,-no-whole-archive g++ -g -o x x.cpp liba.so /tmp/cc6UYIAF.o: In function `main': /data/ld/x.cpp:5: undefined reference to `foo()' collect2: ld returned 1 exit status make: *** [x] Error 1 |
默认情况下,对于未使用到的符号(函数是一种符号),链接器不会将它们链接进共享库和可执行程序。
这个时候,可以启用链接参数“--whole-archive”来告诉链接器,将后面库中所有符号都链接进来,参数“-no-whole-archive”则是重置,以避免后面库的所有符号被链接进来。
// Makefile
all: x x: x.cpp liba.so g++ -g -o $@ $^ liba.so: liba.a g++ -g -fPIC -shared -o $@ -Wl,--whole-archive $^ -Wl,-no-whole-archive liba.a: a.o ar cru $@ $^ a.o: a.cpp g++ -g -c $^ clean: rm -f x a.o liba.a liba.so |
附4:如何让有些“-l”链接静态库,而另一些链接共享库?
用“-Wl,-Bstatic”指定链接静态库,使用“-Wl,-Bdynamic”指定链接共享库,使用示例:
-Wl,-Bstatic -lmysqlclient_r -lssl -lcrypto -Wl,-Bdynamic -lrt -Wl,-Bdynamic -pthread -Wl,-Bstatic -lgtest |
"-Wl"表示是传递给链接器ld的参数,而不是编译器gcc/g++的参数。
附5:相关博文
1) 链接静态库的顺序问题
https://blog.csdn.net/Aquester/article/details/7780640
2) 再议GCC编译时的静态库依赖顺序问题
https://blog.csdn.net/Aquester/article/details/48547685
3) 如何让有些“-l”链接静态库,而另一些链接共享库?
http://blog.chinaunix.net/uid-20682147-id-5096676.html
4) 小心两个共享库共用同一个静态库
http://blog.chinaunix.net/uid-20682147-id-3760647.html
5) C/C++常见gcc编译链接错误解决方法
http://blog.chinaunix.net/uid-20682147-id-5037113.html
6) CMake使用技巧集
http://control.blog.chinaunix.net/uid-20682147-id-5284633.html
7) libtool的工作原理
https://blog.csdn.net/aquester/article/details/23339825
8) 全局变量相互依赖和初始化顺序的解决办法
https://blog.csdn.net/aquester/article/details/7780844
9) 对于glog中ShutdownGoogleLogging后不能再次InitGoogleLogging问题的解决办法
http://blog.chinaunix.net/uid-20682147-id-3449454.html
// 如果不想依赖:
// libc.so, libgcc_s.so, librt.so, libpthread.so, libdl.so, libz.so, librt.so, libstdc++.so等,
// 链接时可指定如下选项(加到命令行最后即可,有些环境还可加上“-static-libstdc++”):
// -Wl,-Bstatic -static-libgcc -lrt -lz -pthread -ldl
// 遇到如下警告,可以忽略:
// warning: Using 'dlopen' in statically linked applications
// warning: Using 'getpwuid_r' in statically linked applications
// warning: Using 'getaddrinfo' in statically linked applications
//
// 检查静态链接效果:
// > ldd -r mooon_ssh
// not a dynamic executable
//
// 但如果是下列错误,则不能采用静态链接(需安装c++标准库的静态库):
// cannot find -lstdc++
//
// 下列错误,可能是因为“/usr/bin/ld: cannot find -lstdc++”:
// the use of `mktemp' is dangerous, better use `mkstemp'
//
// libstdc++.a可能所在位置(编译器版本要和库版本保持相同,否则可能不兼容):
// /usr/lib/gcc/i586-suse-linux/4.1.2/libstdc++.a
// /usr/lib/gcc/x86_64-redhat-linux/4.8.2/32/libstdc++.a
// /usr/lib/gcc/x86_64-redhat-linux/4.4.7/libstdc++.a
// /usr/lib/gcc/x86_64-redhat-linux/4.4.7/32/libstdc++.a
//
// libc.a可能所在位置:
// /usr/lib/libc.a
// /usr/lib64/libc.a
// /usr/lib/x86_64-redhat-linux6E/lib64/libc.a
CMake和静态库顺序的更多相关文章
- cmake opencv,dlib 编译静态库 1
无论windows,linux 所有的库 ,dlib,opencv 通过cmake-gui 设置好静态库, 动态库,和其他各个选项 Tips: cmake 优先级用cmake-gui,因为命令太多,容 ...
- Linux 使用静态库注意事项
1. 静态库一定要放在生成文件后面 gcc main.c -o main libhello.a 2. 使用静态库时一定要连接所有用到的静态库 gcc main.c -o main liba.a lib ...
- 《CMake实践》笔记三:构建静态库(.a) 与 动态库(.so) 及 如何使用外部共享库和头文件
<CMake实践>笔记一:PROJECT/MESSAGE/ADD_EXECUTABLE <CMake实践>笔记二:INSTALL/CMAKE_INSTALL_PREFIX &l ...
- gcc/g++动态链接库和静态库的链接顺序
转自:http://withc8212.blog.163.com/blog/static/11656983820109263562854/ so文件:动态库a文件: 静态库exe文件:可执行程序(li ...
- gcc中动态库和静态库的链接顺序
so文件:动态库a文件: 静态库exe文件:可执行程序(linux下以文件属性来标示是否是可执行文件,与后缀名无关) 经过自己写的一些测试程序,大致了解了下gcc中链接顺序问题,总结出以下几点:1,动 ...
- (转)HelloWorld CMake CMake中构建静态库与动态库及其使用
继续完善Hello World,建立它的共享库, 包括静态库和动态库. 本节的任务: 1,建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc 向终端输出Hello ...
- cmake的使用二:链接第三方静态库
cmake的使用二:链接第三方静态库
- 1. CMake 系列 - 从零构建动态库和静态库
目录 1. 文件目录结构 2. 库文件源代码 3. 编译生成库文件 1. 文件目录结构 首先创建如下目录结构: └── lib ├── build # ├── CMakeLists.txt └── s ...
- cmake 添加头文件目录,链接动态、静态库(转载)
来源网址:http://www.cnblogs.com/binbinjx/p/5626916.html 罗列一下cmake常用的命令. CMake支持大写.小写.混合大小写的命令. 1. 添加头文件目 ...
随机推荐
- EntityFramework的linq扩展where
代码 using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; ...
- MVC是架构模式,而不是设计模式
最早学编程的时候看过一些书,印象深刻的一本书<设计模式解析>,那本书给我后来的工作提供了很大的帮助. 他叫我站在问题模型的立场上指定解决方法,也教会了我软件设计中每个问题都可以细化到到不可 ...
- 9.11 h5日记
9.11 超链接标签<a></a>十分特殊改a标签内容的字体颜色,必须是直接给a 设置,给它的父级标签设置是不可行的. PS:什么是属性继承,即父级标签设置的样式后 ...
- Linux locales
一.简介 二.语法 三.实例 aptitude install locales dpkg-reconfigure locales ; vi /etc/default/locale more / ...
- (转)Eclipse中需要查看某个类的源码,直接按住Ctrl 然后点击想要查看的类或则方法
文章转自:http://blog.sina.com.cn/s/blog_52f623240102vpcr.html 在Eclipse中需要查看某个类的源码,直接按住Ctrl 然后点击想要查看的 ...
- MySql5.5 SQL优化 慢查询日志存储
一.MySql的慢查询日志的开启和存储 1.查看是否把没有使用索引的SQL记录到慢查询日志中,查看 log_queries_not_using_indexes 变量; show VARIABLES L ...
- grep如何进行正则表达式查找
字符类 字符类的搜索:如果我想要搜寻 test 或 taste 这两个单字时,可以发现到,其实她们有共通的 't?st' 存在-这个时候,我可以这样来搜寻: [root@www ~]# grep -n ...
- Luogu 2577[ZJOI2005]午餐 - 动态规划
Solution 啊... 我太菜了唔 不看题解是不可能的, 这辈子都不可能的. 首先一个队伍中排队轮到某个人的时间是递增的, 又要加上吃饭时间, 所以只能使吃饭时间递减, 才能满足最优,于是以吃饭时 ...
- MySQL连接、登录、密码等
官方教程:https://dev.mysql.com/doc/refman/8.0/en/ 链接数据库,通过指定 -h 参数可以连接网络上的数据库 mysql -u 用户名 -h 服务器IP -P 端 ...
- DNSlog实现Mysql注入
step1: 通过DNSlog盲注需要用到load_file()函数.show variables like '%secure%' 查看load_file()可以读取的磁盘. 1.当secure_fi ...