目录

目录 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

最新文章

  1. Python读写csv
  2. NXP恩智浦P89V52X2单片机破解P89C52X2BA芯片解密技术分享!
  3. Linux安装MySql.Data for mono
  4. neon指令,注意事项
  5. 不可或缺 Windows Native (9) - C 语言: 动态分配内存,链表,位域
  6. bootstrap multiselect两大组件
  7. PHP描述冒泡排序和快速排序算法
  8. MySQL 5.7 SYS系统SCHEMA
  9. FreeSWITCH第三方库(视频)的简单介绍(二)
  10. Fixing Poor MySQL Default Configuration Values
  11. [iOS微博项目 - 2.6] - 获取微博数据
  12. 【转】Ubuntu下配置支持Windows访问的samba共享
  13. spring集成guava的event bus
  14. JQuery 获取checkbox被选中的值
  15. 开启 .NET Core 的新篇章
  16. Android For JNI(一)——JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序,使用C启动JAVA程序
  17. Codeforces Round #479 (Div. 3) C. Less or Equal
  18. Flutter 即学即用系列博客——08 MethodChannel 实现 Flutter 与原生通信
  19. windows 子系统 linux wsl 开启ssh 服务
  20. tensorflow安装过程cpu版-(windows10环境下)---亲试可行方案

热门文章

  1. idea中lombok安装
  2. 【WH】MVC数据分页扩展类
  3. MySQL 5.6.4 中TIMESTAMP with implicit DEFAULT value is deprecated 错误
  4. 转录本组装软件StringTie的使用说明
  5. Raw Socket(原始套接字)实现Sniffer(嗅探)
  6. JS浏览器Session存取数据
  7. Spring 常见注解
  8. sqlserver 数据迁移
  9. 提升HTML5的性能体验系列之四 使用原生UI
  10. 在Tomcat中部署Spring jpetstore