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 [email protected] $^

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 [email protected] -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 [email protected] $^

liba.so: liba.a

g++ -g -fPIC -shared -o [email protected] $^

#g++ -g -fPIC -shared -o [email protected] -Wl,--whole-archive $^ -Wl,-no-whole-archive

liba.a: a.o

ar cru [email protected] $^

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 [email protected] $^

liba.so: liba.a

g++ -g -fPIC -shared -o [email protected] -Wl,--whole-archive $^ -Wl,-no-whole-archive

liba.a: a.o

ar cru [email protected] $^

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

原文地址:https://www.cnblogs.com/aquester/p/10084070.html

时间: 2024-10-10 04:36:14

CMake和静态库顺序的相关文章

CMake生成静态库

目录结构 文件内容 Hello.h #ifndef MYSHAREDLIB_HELLO_H #define MYSHAREDLIB_HELLO_H // 打印 Hello World! void hello(); // 使用可变模版参数求和 template <typename T> T sum(T t) { return t; } template <typename T, typename ...Types> T sum(T first, Types ... rest) { r

(转)HelloWorld CMake CMake中构建静态库与动态库及其使用

继续完善Hello World,建立它的共享库, 包括静态库和动态库. 本节的任务: 1,建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc 向终端输出Hello World字符串. 2,安装头文件与共享库. 3, 编写一个程序使用创建的共享库(静态库和动态库). cd /home/ccj/CMakeDemo mkdir t3   cd /backup/cmake/t3 mkdir lib   在t3目录下建立CMakeLists.txt,内容如下: PRO

静态库链接时的依赖关系和先后顺序

1 静态库链接时的依赖关系 静态库之间是有依赖关系的,比如A.lib依赖于B.lib.C.lib.D.lib.E.lib,因为A会使用B.C.D.E中的函数. 2 使用静态库时的先后顺序 是不是B.C.D.E一定要放在A的后面?或者说,如果自己依赖于某个库,那么自己就要放在这个库的后面? 如果是的话,为什么会这样?是所有的链接器都是这样的吗? MSVC++12.0编译器是不管这个顺序的,gcc的未知. 3 使用静态库时必须注意依赖关系 也就是说,要使用一个lib时,要把它依赖的所有的lib都加上

cmake同时生成动态库与静态库的方法

我的目录结构 [[email protected] createLibrary]$ tree . ├── bin ├── build ├── CMakeLists.txt ├── include │   └── person.h ├── lib └── src ├── CMakeLists.txt ├── main │   ├── CMakeLists.txt │   └── main.cpp └── person ├── CMakeLists.txt └── person.cpp 7 dire

CMake同时生成静态库和动态库

环境:Linux CMake 修改CMakeLists.txt 同时将模块编译生成动态库和静态库的方法: ADD_LIBRARY(nyutil SHARED ${SRC_LIST}) ADD_LIBRARY(nyutil_static STATIC ${SRC_LIST}) SET_TARGET_PROPERTIES(nyutil_static PROPERTIES OUTPUT_NAME "nyutil") SET_TARGET_PROPERTIES(nyutil PROPERTIE

HelloWorld CMake Demo 03:CMake中构建静态库与动态库及其使用

? 继续完善Hello World,建立它的共享库, 包括静态库和动态库. ? 本节的任务: 1,建立一个静态库和动态库,提供HelloFunc函数供其他程序编程使用,HelloFunc 向终端输出Hello World字符串. 2,安装头文件与共享库. 3, 编写一个程序使用创建的共享库(静态库和动态库). ? 一,准备工作: 在/home/ccj/CMakeDemo目录建立t3目录,用于存放本节涉及到的工程. cd /home/ccj/CMakeDemo mkdir t3 ? 二,建立共享库

gcc/g++动态链接库和静态库的链接顺序

转自:http://withc8212.blog.163.com/blog/static/11656983820109263562854/ so文件:动态库a文件: 静态库exe文件:可执行程序(linux下以文件属性来标示是否是可执行文件,与后缀名无关) 经过自己写的一些测试程序,大致了解了下gcc中链接顺序问题,总结出以下几点:1,动态库中可以包含另一个静态库,通过参数 -lxxx 把静态库libxxx.a加入so文件中,这样so文件中   就包含了libxxx.a的所有实现.当然,如果不包

CMake - boost - 可执行程序 - 静态库

CMake - boost 最后更新日期:2014-04-25by kagula 阅读前提:<CMake入门(二)>.Linux的基本操作 环境: Windows 8.1 64bit英文版,Visual Studio 203 Update1英文版,CMake 2.8.12.2. Cent OS 6.5.Cent OS 6.5自带gcc 4.4.7.icu 4.2.1 内容简介 介绍如果在VisualStudio上的项目依赖于boost库,如何使用cmake工具把这个项目移到Cent OS上.

android ndk-build 编译静态库libxx.a 以及Android studio openssl 静态库配置(cmake)

android ndk-build 编译静态库libxx.a 需求场景: 目前有安卓编码好的现在的openssl的两个.a,我们需要调用openssl的函数,并把功能再封装成.a; 这样使用时,在android studio jni项目 cmake 里面,需要先引用openssl的.a再引用 上面封装的.a; 如果使用so,那么,直接在android studio jni项目的 cpp里面直接调用openssl的方法,对外提供jni java接口,打包成so即可: 先来说用ndk-build 打