静态和动态链接中的库顺序

时间:2016-05-27 17:57:52

标签: c++ boost linker g++ static-linking

我正在尝试构建一些使用boost库的示例c ++代码。我使用this作为静态链接的参考示例。

当我使用动态库构建时,一切都很好。

g++  -Wall -std=c++0x -O3 -Wfatal-errors -I/usr/include/boost/include  -c -o src/main.o src/main.cpp
g++  -Wall -std=c++0x -O3 -Wfatal-errors -I/usr/include/boost/include  -c -o src/ThreadExample.o src/ThreadExample.cpp
g++  -Wall -std=c++0x -O3 -Wfatal-errors -I/usr/include/boost/include  -c -o src/Utils.o src/Utils.cpp
g++ src/main.o src/ThreadExample.o src/Utils.o -lboost_thread -lboost_filesystem -lboost_system -lboost_timer -o ThreadExampleBinary

但是当我使用静态库时,我会遇到很多undefined reference错误:

g++  -Wall -std=c++0x -O3 -Wfatal-errors -I/usr/include/boost/include  -c -o src/main.o src/main.cpp
g++  -Wall -std=c++0x -O3 -Wfatal-errors -I/usr/include/boost/include  -c -o src/ThreadExample.o src/ThreadExample.cpp
g++  -Wall -std=c++0x -O3 -Wfatal-errors -I/usr/include/boost/include  -c -o src/Utils.o src/Utils.cpp
g++ -static src/main.o src/ThreadExample.o src/Utils.o -lboost_thread -lboost_filesystem -lboost_system -lboost_timer -o ThreadExampleBinary

/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libboost_timer.a(cpu_timer.o): In function `boost::timer::cpu_timer::start()':
(.text+0x7fd): undefined reference to `boost::chrono::steady_clock::now()'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libboost_timer.a(cpu_timer.o): In function `boost::timer::cpu_timer::stop()':
(.text+0x94c): undefined reference to `boost::chrono::steady_clock::now()'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libboost_timer.a(cpu_timer.o): In function `boost::timer::cpu_timer::elapsed() const':
(.text+0xa59): undefined reference to `boost::chrono::steady_clock::now()'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libboost_timer.a(cpu_timer.o): In function `boost::timer::cpu_timer::resume()':
(.text+0xb60): undefined reference to `boost::chrono::steady_clock::now()'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libboost_timer.a(cpu_timer.o): In function `boost::timer::auto_cpu_timer::auto_cpu_timer(std::ostream&, short)':
(.text+0xca5): undefined reference to `boost::chrono::steady_clock::now()'
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libboost_timer.a(cpu_timer.o):(.text+0xd4e): more undefined references to `boost::chrono::steady_clock::now()' follow
collect2: error: ld returned 1 exit status
make: *** [ThreadExampleBinary] Error 1

似乎可以修复添加额外的-lboost_chrono库。 但为什么它在dinamic环境中起作用?

3 个答案:

答案 0 :(得分:3)

使用静态链接时,您还必须静态链接到要链接的库所依赖的任何库。

答案 1 :(得分:2)

不同之处在于共享库在ELF头中有一个名为NEEDED的条目,该条目列出了在链接到此文件时要包含的其他共享库。

您可以使用以下命令查看它们:

$ objdump -p /usr/lib/libboost_timer.so | grep NEEDED
  NEEDED               libboost_chrono.so.1.60.0
  NEEDED               libboost_system.so.1.60.0
  NEEDED               librt.so.1
  NEEDED               libstdc++.so.6
  NEEDED               libgcc_s.so.1
  NEEDED               libc.so.6

但是对于静态库,没有这样的系统,因为它们只是对象文件的集合。

值得注意的是,共享对象中的NEEDED条目完全是可选的,如果它们不可用,那么它们的行为与静态条件完全相同。但是大多数共享库都包含它们。

许多库使用pkg-config基础结构来提供所需的完整命令行,但AFAIK提升不是其中之一。

如何自动完成此过程?好吧,你没有。您只需包含所需内容并按照链接器错误发现更多需求。

您可以找到哪个静态库包含符号类似的符号:

$ nm --print-file-name --defined-only --demangle /usr/lib/*.a  2> /dev/null | \
           grep -q 'boost::chrono::steady_clock::now()'
/usr/lib/libboost_chrono.a:chrono.o:0000000000000090 T boost::chrono::steady_clock::now()

答案 2 :(得分:0)

  

但为什么它在dinamic环境中起作用?

我的大多数make文件都有以下注释(从我需要时找到答案后...抱歉,我不知道我在哪里找到它。)

注意 - 当使用'-l'的构建找到该库的.so版本(所以 - 共享对象)并且同样存在.a存档时,g ++优先于.a。

但是,您仍然可以通过完全指定.a。

的路径来实现静态链接

示例:

$(CC) $(CC_FLAGS)  $<  /usr/local/lib/libboost_chrono.a  -o $@  ...
#                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

有时,存档代码会引用另一个存档中的符号。

即。 -lyyy_i686使用-lxxx_i686中的一些函数,并且在构建命令中首先列出了xxx。符号可能仍未解析且链接器失败。

发生这种情况时,请尝试再次将xxx添加到存档列表的末尾

   from:      -lxxx_i686   -lyyy_i686  -lrt -pthread

   becomes    -lxxx_i686   -lyyy_i686  -lrt -pthread -lxxx_i686
               ^^^^^^^^^_____________________________^^^^^^^^^^

前面假设只有.a库是可以找到的(没有xxx.so)

另一种方法是,您可以命令链接器多次搜索存档

                        -lyyy_i686  -lrt -pthread       -(-lxxx_i686-)
tell gcc to link this library as many times as needed __^^__________^^

同样,只有.a是可以找到的

最后,如果您选择与.so链接,请注意,这不会将整个lib拉入当前构建目标。相反,在运行时,.so被加载到内存中,如果它还没有。程序启动后会发生这种情况。对于大多数应用程序,当程序调整其内存映射(在后台自动化)时,这被视为“小”启动性能。我相信我曾经发现应用程序本身可以控制何时加载.so。

使用.so将整个库拉入内存(如果尚未安装)。因此,在上面的xxx vs yyy场景中,没有任何遗漏符号,.so会拉出所有符号,无论是否使用。

再一次,当尝试使用“-lxxx_i686”加载xxx时,即使libxxx_i686.a位于同一目录中,链接器也更喜欢引入libxxx_i686.so。